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.