Possible heap corruption, debugging with valgrind - c

I'm working on a project that makes use of a string buffer. I've been getting random errors with free() and malloc() - Like "invalid next size (fast)" and suspects if it is due to some memory heap corruption. I'm using gcc. I used valgrind on the binary file and this is the summary :
ERROR SUMMARY: 26887 errors from 39 contexts (suppressed: 0 from 0)
I think that's a bit too high. I'm attaching a pastebin of the valgrind memcheck output here
Most of the problems seem to be from a single function : strbuf_addc(). strbuf is a string buffer that can grow automatically. I'm pasting some strbuf functions here.
int strbuf_add(struct strbuf *string, const char *c)
{
if(string == NULL || c == NULL) return 0;
while(*c != '\0') {
if(!strbuf_addc(string, *c++))
return 0;
}
return 1;
}
int strbuf_addc(struct strbuf *string, char c)
{
size_t space_available;
assert(string != NULL);
space_available = string->allocated - string->length;
if(space_available <= 1) {
if(!grow_buffer(string)) {
return 0;
}
}
string->buffer[string->length++] = c;
string->buffer[string->length] = '\0';
return 1;
}
static int grow_buffer(struct strbuf *string)
{
char *tmp;
size_t toallocate;
assert(string != NULL);
toallocate = string->allocated + (string->allocated / 2);
tmp = (char*) realloc(string->buffer, toallocate);
if(tmp) {
string->buffer = tmp;
string->allocated = toallocate;
return 1;
}
return 0;
}
I'm not sure if strbuf_addc is the culprit or some other function that I wrote. Please take a look. I am basically passing string literals as the second argument to strbuf_add. I'm not sure if they will be null terminated, but I suppose string literals in c are null terminated. I've also tried reading strings from a file, still some errors.

toallocate = string->allocated + (string->allocated / 2);
there might be situations where toallocate won't be bigger than string->allocated. so, realloc won't reserve more space for your string and you won't be able to add a character. valgrind keeps saying that :
==4755== Invalid write of size 1
so you just don't have space to append a char.

Related

My own substring function | valgrind showing some malloc errors I do not understand

Task:
Allocate (with malloc(3)) and return a substring from the string s. The substring begins at index start and is of maximum size len.
Return value: The substring. NULL if the allocation fails.
Hello, after a few hours I decided to ask for some clarifications. I have the following functions and some error from Valgrind I can't understand, that shows up even if everything is correct. (ft_strlen(s) I call from my own library, where also lib for malloc is put).
char *ft_substr(char const *s, unsigned int start, size_t len)
{
unsigned int x;
char *a;
unsigned int i;
i = 0;
if (s == NULL)
return (0);
if (start > ft_strlen(s))
{
if (!(a = (char *)malloc(0*sizeof(char))))
return (0);
return (a);
}
if ((start + len) < ft_strlen(s))
x = len;
else
x = ft_strlen(s) - start;
if (!(a = (char *)malloc((x + 1) * sizeof(char))))
return(0);
while (i < x)
{
a[i] = s[start + i];
i++;
}
a[i] = '\0';
return (a);
}
I left there one error on purpose. If I am suppose to return null if allocation fails, why below instead of 0 should be 1? Anyway it does not change the errors presented below.
if (!(a = (char *)malloc(0 * sizeof(char))))
ERRORS:
==4817== Invalid read of size 1
==4817== at 0x483FED4: strcmp (in /usr/lib/x86_64-linux-gnu/valgrind vgpreload_memcheck-amd64-linux.so)
==4817== by 0x4039BC: main (ft_substr_test.cpp:28)
==4817== Address 0x4dad0d0 is 0 bytes after a block of size 0 alloc'd
==4817== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==4817== by 0x403B58: ft_substr (in /home/tony/42cursus/0lvl_libft_1week/libftTester/a.out)
==4817== by 0x4039A4: main (ft_substr_test.cpp:27)
==4817==
Your function has multiple problems:
the type of start, x and i should be size_t.
malloc(0) has implementation defined behavior. You should allocate at least 1 byte for the null terminator and set it before returning the pointer to the empty string or return NULL if the specification says you should.
the function should call ft_strlen() just once.
the special case for start > ft_strlen(s) can be handled in the general case if an empty string should be returned.
Here is a modified version:
char *ft_substr(char const *s, size_t start, size_t len) {
size_t i, slen;
char *a;
if (s == NULL) {
return NULL;
}
slen = ft_strlen(s);
if (start > slen) {
start = slen;
}
if (len > slen - start) {
len = slen - start;
}
if (!(a = malloc((len + 1) * sizeof(char)))) {
return NULL;
}
for (i = 0; i < len; i++) {
a[i] = s[start + i];
}
a[i] = '\0';
return a;
}
PS: you may need to reformat the code to fit the local 42 norminette...
On Linux systems, calling malloc(0) will not necessarily return a NULL pointer. It could return a pointer that your can't write to but can pass to free.
So when you return the result of malloc(0) from the function the calling function sees a non-null pointer and attempts to dereference it. Since this pointer essentially points to a buffer of size 0, attempting to read it reads past the end of the buffer, which is what valgrind is complaining about.
You can fix this by either returning NULL:
if (start > ft_strlen(s))
{
return NULL;
}
Or by allocating space for an empty string and setting the null byte:
if (start > ft_strlen(s))
{
if (!(a = malloc(1)))
return NULL;
*a = 0;
return a;
}
A few other notes:
sizeof(char) is defined to be 1, so you can leave it out of size calculations
Don't cast the return value of malloc.
Use NULL instead of 0 for null pointers
Parenthesis aren't required around the expression in a return statement.

strange free() invalid pointer C

I have a problem with this piece of code that I modified many times (but the error always appeared):
It seems it has an error in freeing the last index of "filter"
char** read_and_filter(int fd) {
char buf[MAXLENGTH];
char **bufs=NULL;
char ch;
int j = 0, len = 0, t = 0;
while (!t && read(fd,&ch,1) == 1) {
switch (ch) {
case '\n':
t = 1;
case ' ':
bufs = realloc(bufs, (j+1)*sizeof(char*));
bufs[j++] = strndup(buf,len);
memset(buf,0,len);
len = 0;
break;
default:
buf[len++] = ch;
}
}
bufs[j] = 0;
return bufs;
}
int main(int argc, char **argv) {
char **filter;
int i,fd = open("input.txt",O_RDONLY);
filter = read_and_filter(fd);
for(i = 0; filter[i]; i++) {
printf("%s\n",filter[i]);
free(filter[i]);
}
return 0;
}
Here is the output:
0x1521030
HOME
0x1521050
2
0x1521070
A
0x1521010
8
0x15210c0
D
*** Error in `./test': free(): invalid pointer: 0x00000000015210c0 ***
I also tried to debug it with valgrind (it says me that the allocator tries to free 9 byte while the sum of characters is 8, strange no?) and gdb but nothing worked.
The first line of input.txt is "HOME 2 A 8 D\n"
The first time these lines are executed
bufs = realloc(bufs, (j+1)*sizeof(char*));
bufs[j++] = strndup(buf,len);
you obtain memory for 1 pointer (j was 0). This leaves no space for the closing NULL you write at the end of the function with
bufs[j] = 0;
so you are writing beyond the allocated memory, thus have undefined behaviour. Similarly each time you extend the buffer length.
Your bufs[j] = 0; at the end of read_and_filter writes into non-allocated memory. You never realloc-ed your bufs for that extra 0.
Memory leak is occurring from two places - strdup and the realloc
One answer is to make an initial allocation of memory for the buffer in main, using malloc and then pass a pointer to the allocated memory to the function. The function can then realloc the buffer, and copy data into it.
On return from the function, main can access the data directly from the buffer as it has a valid pointer to it, and then can free that memory before closing.
According to valgrind, the following has no memory loss.
void read_and_filter(int fd, char **bufs) {
char buf[100];
char ch;
int j = 0, len = 0, t = 0;
while (!t && read(fd,&ch,1) == 1) {
switch (ch) {
case '\n':
t = 1;
case ' ':
*bufs = realloc(*bufs, (j + 2)*sizeof(char*));
strncpy(bufs[j++], buf, len);
memset(buf,0,len);
len = 0;
break;
default:
buf[len++] = ch;
}
}
bufs[j] = 0;
return;
}
int main(int argc, char **argv) {
char *bptr = malloc(1);
int fd = open("input.txt", O_RDONLY);
read_and_filter(fd, &bptr);
printf("%s\n", bptr);
free(bptr);
return 0;
However I cannot be sure that this fully replicates the OP's intended functionality, but the overall approach does deal with the memory issues.

Segmentation Fault in Vertical Redundancy Check

I am trying to make program in C for vertical redundancy check. The Code is given below :
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
int fd,i;
char *data = "01010101010111110101010101011111";
int count = 0,bit_count=0;
char *parity_bit_array = NULL;
char *data_to_send = NULL;
char *stream_name = "Named Stream";
do
{
if(*data == '1' && bit_count <= 8)
{
count++;
if(bit_count == 8)
{
if( count % 2 == 0)
{
*parity_bit_array = '1';
count = 0;
bit_count = 0;
}
else
{
*parity_bit_array = '0';
count = 0;
bit_count = 0;
}
}
}
bit_count++;
data++;
} while( !data);
do
{
if(bit_count <= 8)
{
*data_to_send++ = *parity_bit_array++;
}
*data_to_send++ = *data;
} while( !data );
printf("%s \n",data_to_send);
mkfifo(stream_name,0666);
fd = open(stream_name,O_WRONLY);
write(fd,data_to_send,sizeof(data_to_send));
close(fd);
unlink(stream_name);
return 0;
}
The file shown below is the sender file of which data is to be read by the receiver.
By using sized array it is working properly but i like to use it with Pointer.
Main Variables in this code :
data : Data on which VRC to be implemented
count : Counting 1 for Even Parity Bit
bit_count : Counting 8 Bits
parity_bit_array : To Collect Parity Bit for Every Single Byte present in data
data_to_send : Combination made by data + parity_bit_array
Ex:
data : 01110000
parity_bit_array : 1
data_to_send : 011100001
You are not allocating memory for your char pointers and you are trying to write to them which will lead to undefined behavior hence segmentation fault.
*parity_bit_array = '1';
There are multiple such cases in this code.
char *data_to_send = NULL;
data_to_send pointer is never allocated memory and you try to write to it
*data_to_send++ = *parity_bit_array++;
Allocate memory to char pointers like
char *data_to_send = malloc(20);
While writing to this array if you see 20 bytes is already written just do realloc() for the same memory
char *temp = realloc(data_to_send,40);
if(temp != NULL)
data_to_send = temp;
There are multiple issues:
} while( !data); is wrong, you should use : } while( *data != 0);
This can indirectly cause segmentation fault(If you are lucky) making the code loop indefinably.
Memory is not allocated to *parity_bit_array and *data_to_send.
Accessing un-allocated memory is undefined behavior and can cause anything including segmentation fault.
write(fd,data_to_send,sizeof(data_to_send)); should be write(fd,data_to_send,sizeof(*data_to_send)); Or something like that as per your logic.

Heap corruption while freeing memory C

This is my code:
int load_data(char *line, int *vertex, int *edge)
{
char *ch_number = NULL;
char *error = NULL;
*vertex = (int)strtol(line, &error ,10);
if((error[0] != '-') && (error[1] != '>')) return 0;
ch_number = (char*)malloc(sizeof(char) * (strlen(error) - 2));
memcpy(ch_number, &error[2], strlen(error) - 2);
ch_number[strlen(error) - 2] ='\0';
*edge = (int)strtol(ch_number, &error ,10);
if(error[0] != '\0')
{
free(ch_number);
return 0;
}
free(ch_number);
return 1;
}
Debugger shows that free(ch_number); makes heap corruption.
What am i doing wrong?
This is the example of using:
load_data("15643->45545546",&vertex,&edge);
C arrays are zero based so the line
ch_number[strlen(error) - 2] ='\0';
writes one byte beyond the end of ch_number. The effects of doing this are undefined but it sounds like you've written to a guard word used by the heap manager to detect exactly this sort of corruption.
If you want to add a null terminator to ch_number, you need to allocate an extra byte of memory
ch_number = malloc(strlen(error) - 1);

Initializing an infinite number of char **

I'm making a raytracing engine in C using the minilibX library.
I want to be able to read in a .conf file the configuration for the scene to display:
For example:
(Az#Az 117)cat universe.conf
#randomcomment
obj:eye:x:y:z
light:sun:100
light:moon:test
The number of objects can vary between 1 and the infinite.
From now on, I'm reading the file, copying each line 1 by 1 in a char **tab, and mallocing by the number of objects found, like this:
void open_file(int fd, struct s_img *m)
{
int i;
char *s;
int curs_obj;
int curs_light;
i = 0;
curs_light = 0;
curs_obj = 0;
while (s = get_next_line(fd))
{
i = i + 1;
if (s[0] == 'l')
{
m->lights[curs_light] = s;
curs_light = curs_light + 1;
}
else if (s[0] == 'o')
{
m->objs[curs_obj] = s;
curs_obj = curs_obj + 1;
}
else if (s[0] != '#')
{
show_error(i, s);
stop_parsing(m);
}
}
Now, I want to be able to store each information of each tab[i] in a new char **tab, 1 for each object, using the ':' as a separation.
So I need to initialize and malloc an undetermined number of char **tab. How can I do that?
(Ps: I hope my code and my english are good enough for you to understand. And I'm using only the very basic function, like read, write, open, malloc... and I'm re-building everything else, like printf, get_line, and so on)
You can't allocate an indeterminate amount of memory; malloc doesn't support it. What you can do is to allocate enough memory for now and revise that later:
size_t buffer = 10;
char **tab = malloc(buffer);
//...
if (indexOfObjectToCreate > buffer) {
buffer *= 2;
tab = realloc(tab, buffer);
}
I'd use an alternative approach (as this is c, not c++) and allocate simply large buffers as we go by:
char *my_malloc(size_t n) {
static size_t space_left = 0;
static char *base = NULL;
if (base==NULL || space_left < n) base=malloc(space_left=BIG_N);
base +=n; return base-n;
}
Disclaimer: I've omitted the garbage collection stuff and testing return values and all safety measures to keep the routine short.
Another way to think this is to read the file in to a large enough mallocated array (you can check it with ftell), scan the buffer, replace delimiters, line feeds etc. with ascii zero characters and remember the starting locations of keywords.

Resources