1

This is a follow-up question to this one.

I fixed double free and memory corruption by adding copy constructor and assignment constructor to class File and Buffer. But that remote server reports there is segmentation fault. Is there any way to cause class File or Buffer segment fault? I think segment fault is normally related to stack. But I don't have stack operation in these 2 classes.

Buffer.h

#ifndef __BUFFER_H__
#define __BUFFER_H__

#include <stdlib.h>
#include <cerrno>
#include <stdio.h>

class Buffer
{

private:
  char * buffer;
  int size;
  Buffer(const Buffer &);
  Buffer& operator=(const Buffer &);

public:
    Buffer(int size);
  ~Buffer();
  void reverse(int size);


    friend class File;
};

#endif

Buffer.cc:

#include "Buffer.h"
#include "Exception.h"


Buffer::Buffer(int size)
{
  this -> size = size;
  this -> buffer = (char *)malloc(size);
  if(this -> buffer == NULL)
    throw Exception(errno);
}

Buffer::~Buffer()
{
  // // if(this -> buffer != NULL)
  // {
  free(this -> buffer);
  //   this -> buffer = NULL;
  // }
}

void Buffer::reverse(int size)
{
  char tmp;
  int i;
  char * tmpb = this -> buffer;
  for(i = 0; i < size / 2; i++)
  {
    tmp = tmpb[i];
    tmpb[i] = tmpb[size - i - 1];
    // printf("exchange %x with %x\n", tmp & 0xff, tmpb[i] & 0xff);

    tmpb[size - i - 1] = tmp;
  }
}

File.h:

#ifndef __FILE_H__
#define __FILE_H__

#include "Buffer.h"
#include "Exception.h"
#include <stdio.h>
#include <cerrno>

class File
{

private:
  FILE * f;
  File(const Buffer &);
  File& operator=(const File &);



public:
  int whence;
    // Note: opening the same file twice for writing ("w")
    // at the same time is forbidden
    File(const char* name, const char *mode);
  ~File();

    int read(Buffer& buffer, int size);
    void write(Buffer& buffer, int size);
    void seek(int pos);
    void close();
  // void seek(long offset, int whence);
  long size();
};

#endif

File.cc:

#include "File.h"


File::File(const char* name, const char *mode)
{
  f = fopen(name, mode);
  if(f == NULL)
    throw Exception(errno);
}

File::~File()
{
  if(f != NULL)
    fclose(f);
}

int File::read(Buffer& buffer, int size)
{
  clearerr(this -> f);
  size_t tmp;
  tmp = fread(buffer.buffer, 1, size, this -> f);

  // printf("%ld bytes read\n", tmp);
  // for(int i = 0; i < tmp; i++)
  //   printf("%x ", buffer.buffer[i] & 0xff);
  // printf("\n");

  if(feof(this -> f) != 0)
    return EOF;
  if(ferror(this -> f) != 0)
    throw Exception(errno);

  return tmp;
}

void File::write(Buffer& buffer, int size)
{
  size_t tmp;
  clearerr(this -> f);
  tmp = fwrite(buffer.buffer, 1, size, this -> f);

  // printf("%ld bytes written\n", tmp);
  // for(int i = 0; i < tmp; i++)
  //   printf("%x ", buffer.buffer[i] & 0xff);
  // printf("\n");

  if(ferror(this -> f) != 0)
    throw Exception(errno);
}

void File::seek(int pos)
{
  int ret = fseek(this -> f, pos, this -> whence);
  if(ret != 0)
    throw Exception(errno);
}

void File::close()
{
  int tmp;
  if(this -> f != NULL)
    tmp = fclose(this -> f);
  this -> f = NULL;
  if(tmp != 0)
    throw Exception(errno);
}

long File::size()
{
  if(fseek(this -> f, 0, SEEK_END) != 0)
    throw Exception(errno);

  long tmp = ftell(this -> f);
  if(tmp == -1)
    throw Exception(errno);

  if(fseek(this -> f, 0, SEEK_SET) != 0)
    throw Exception(errno);
  return tmp;
}

NOTE: I have to use C style code. Otherwise I would fail the server test. That is a hard requirement. Well, you may think this requirement is silly. But this is the requirement. Maybe one point is learning the bad while someone mixing C and C++.

The server provides a main function to test my implementation. Just compile using make. The result is a program named rcopy which reverses content of a file byte by byte and then outputs to a new file.

Here is the detailed error output:

make: Entering directory `/home/vmcheck/testhome/co/rcopy'
g++ -c rcopy.cc
g++ -c Buffer.cc
g++ -c Exception.cc
g++ -c File.cc
g++ rcopy.o Buffer.o Exception.o File.o -o rcopy
make: Leaving directory `/home/vmcheck/testhome/co/rcopy'
======== COMPILING AGAINST OUR TESTS ========
g++ -c -Wall -I./   t1.cc -ot1.o
g++  -ot1  t1.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t2.cc -ot2.o
g++  -ot2  t2.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t3.cc -ot3.o
g++  -ot3  t3.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t4.cc -ot4.o
g++  -ot4  t4.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t5.cc -ot5.o
g++  -ot5  t5.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t6.cc -ot6.o
g++  -ot6  t6.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   t7.cc -ot7.o
g++  -ot7  t7.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   f1.cc -of1.o
gcc failed_read.c -ldl -shared -fPIC -o failed_read.so
gcc failed_write.c -ldl -shared -fPIC -o failed_write.so
failed_write.c:5:7: warning: conflicting types for built-in function ‘fwrite’ [enabled by default]
g++  -of1  f1.o Buffer.o Exception.o File.o
g++ -c -Wall -I./   f2.cc -of2.o
g++  -of2  f2.o Buffer.o Exception.o File.o

========= TESTING RCOPY ==========
Run: large file
size of input file: 16473
Run: small file
size of input file: 0

========= TESTING EXCEPTION BEHAVIOUR ==========
*** Test 1 ***
*** Test 2 ***
*** Test 3 ***
bash: line 1: 22041 Segmentation fault      ./t3
FAILED

The bash script isn't source of the segment fault. I can confirm this. Noted that the test serve could provide many various versions of buggy main to test File, Buffer and Exception.

Community
  • 1
  • 1
JACK M
  • 2,627
  • 3
  • 25
  • 43
  • *"I think segment fault is normally related to stack"* - uh, no? What makes you think that? – UnholySheep Nov 16 '16 at 14:36
  • Also, have you actually been able to reproduce the crash? (e.g.: what exactly does the `main` that tests your implementation do?) If yes, where does it occur? – UnholySheep Nov 16 '16 at 14:44
  • @UnholySheep I dont have access to that server. SO honestly I dont know how to answer your question. All I know is that it tests my code using fault injection. It compiles various main program, includes my implementation of File, Buffer and Exception. If the test fails, it gives me some error megs. That test server focus testing on exceptional hehaviours, but no specific instruction tells me what exactly is under tested. – JACK M Nov 16 '16 at 14:53
  • @UnholySheep That is the hard part. It's very blind. I dont even know which line of my code goes wrong. – JACK M Nov 16 '16 at 14:54
  • 1
    Are you sure you are properly initializing File's whence in a constructor? Is it being 0 by default something you want? – SenselessCoder Nov 16 '16 at 15:19
  • I do not see any test about the buffer size inside your File class. Maybe the call to the read/write functions is wrong and the parameter size is larger than the buffer size. – Ionel POP Nov 16 '16 at 17:08
  • OT: `#include ` has been deprecated for decades and your include guards are plain illegal. You should get better and more up-to-date learning material. – Baum mit Augen Nov 16 '16 at 21:29
  • The easiest way to track down this kind of problem is usually to run the program in gdb. Compile with the -O0 and -g options to make debugging easier. Then launch `gdb t3`, type `run` at the prompt and it should stop when you get the segfault and return you to the gdb prompt. Use the `bt` command to see where you are. – Waxrat Jan 08 '17 at 16:50
  • Another way to track down this kind of problem is to run the program under valgrind. Again, compile with `-O0 -g` to make debugging easier, then run `valgrind t3`. – Waxrat Jan 08 '17 at 16:51

1 Answers1

1

A possible source of crash are the read and write methods, when called with invalid parameters. For instance, read can be called with a buffer having a size 10, but the function read is requested to read 20 bytes. In this case, you will overflow your buffer.

You have two solutions: either you change your buffer class to be able to resize dinamically, either you read/write up to the maximal size of the buffer, for instance:

tmp = fread(buffer.buffer, 1, min(size, buffer.size), this -> f);

It goes the same for the write.

Ionel POP
  • 743
  • 7
  • 12