It appears that when the realloc function is called with a pointer to non-heap memory, the program dies with
Error(s):
Invalid memory reference (SIGSEGV)
Is it possible for my str_cat function to detect this case and thus avoid calling realloc
Here is a sample program which demonstrates the problem:
//gcc 5.4.0
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int str_cat(char *dest, size_t *dest_size, char *src);
int str_cat(char *dest, size_t *dest_size, char *src) {
// if there is sufficient free space in the dest buffer, append src,
// otherwise keep doubling the allocated size of dest until it is large
// enough to append src
size_t src_len = strlen(src);
size_t dest_len = strlen(dest);
if (src_len < *dest_size - dest_len) {
memcpy(dest+dest_len, src, src_len+1);
return 0;
} else {
char *new_dest = NULL;
size_t new_size = *dest_size * 2;
while (src_len >= new_size - dest_len) {
new_size *= 2;
}
if ((new_dest = realloc(dest, new_size)) != NULL) {
dest = new_dest;
*dest_size = new_size;
memcpy(dest+dest_len, src, src_len+1);
return 0;
} else {
return -1;
}
}
}
int main(void)
{
size_t heap_buf_size=5;
char *heap_buf = malloc(heap_buf_size);
size_t stack_buf_size=5;
char stack_buf[stack_buf_size];
*heap_buf = '\0';
if (str_cat(heap_buf, &heap_buf_size, "foo")) return -1;
printf("1. heap_buf %s\n", heap_buf);
printf("1. heap_buf_size %zu\n", heap_buf_size);
if (str_cat(heap_buf, &heap_buf_size, "bar")) return -1;
printf("2. heap_buf %s\n", heap_buf);
printf("2. heap_buf_size %zu\n", heap_buf_size);
stack_buf[0] = '\0';
if (str_cat(stack_buf, &stack_buf_size, "foo")) return -1;
printf("3. stack_buf %s\n", stack_buf);
printf("3. stack_buf_size %zu\n", stack_buf_size);
/* If the line below is uncommented, the program dies
with Invalid memory reference (SIGSEGV) */
// if (str_cat(stack_buf, &stack_buf_size, "bar")) return -1;
printf("4. stack_buf %s\n", stack_buf);
printf("4. stack_buf_size %zu\n", stack_buf_size);
return 0;
}
No, it's not possible to catch or handle SIGSEGV on most systems in a meaningful way. This is because at the point when the program causes unmapped memory access it probably have damaged its stack or data or code already, so can't be trusted and is beyond saving. You're better off crashing immediately if you detect such situation so you can analyze dumped core in debugger.
To track invalid access valgrind is a nice tool, try valgrind ./a.out, will run slow but track many things, including misuses of malloc/realloc/free.
Edit: (since OP edited the question) No, it's not possible to detect if pointer is a correct argument for realloc/free, unless you track all (de)allocations (say using malloc hooks).
Related
I'm new in c and sorry for my poor English.
I'm trying to write a program that ask to user if he want enter data (region, date of detection, mm of rain) using keyboard and save it in file or if he want give it file's name.
No problem at this time and file is written or read correctly.
File have this structure:
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
....
Try with scanf() (not report main because there is no problem in it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese;
typedef struct data_s
{
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s
{
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s
{
tipo_dati_file* array;
int count;
} tipo_ritorna;
int conta_righe(char* Nome_f)
{
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
}
void crea_array (char* Nome_f)
{
int i,n;
char* regione= (char*)malloc(sizeof(char));
tipo_data data;
int mm_pioggia;
tipo_ritorna risultati;
FILE* file;
n = conta_righe(Nome_f);
printf("%d\n",n);
tipo_dati_file* array = (tipo_dati_file*) malloc (n*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
for(i=0; i<=n; i++)
{
fscanf(file,"%s %d/%d/%d %d\n",regione, &data.giorno, &data.mese, &data.anno, &mm_pioggia);
strcpy(array[i].regione, regione);
array[i].data.giorno=data.giorno;
array[i].data.mese= data.mese;
array[i].data.anno= data.anno;
array[i].mm_pioggia= mm_pioggia;
printf("%s %d/%d/%d %d\n",array[i].regione,array[i].data.giorno, array[i].data.mese,array[i].data.anno,array[i].mm_pioggia);
}
fclose(file);
}
try with fgets()
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum mese_e {Gen=1, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic} tipo_mese; typedef struct data_s {
int giorno;
tipo_mese mese;
int anno; } tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia; } tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count; } tipo_ritorna;
int conta_righe(char* Nome_f) {
int i=0;
char c;
FILE* file;
file=fopen(Nome_f,"r");
while ((c=fgetc(file))!=EOF)
{if(c=='\n')
i++;}
fclose(file);
return i;
} void crea_array (char* Nome_f, int v) {
int i=0,s;
char* r;
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file* array = (tipo_dati_file*) malloc (v*sizeof (tipo_dati_file));
file = fopen(Nome_f,"r");
if( file==NULL )
{
printf("Errore in apertura del file!");
exit(1);
}
if (feof(file)==0)
{
char* buf= (char*) malloc(v*sizeof(char));
/*while ( fgets( buf,10000, file) != NULL )
{
r = sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}*/
while(1)
{
r=fgets( buf,1000, file);
if (r!=NULL)
{
printf("%s",buf);
sscanf( buf, "%s% d/%d/%d %d\n", array[i].regione, &array[i].data.giorno, &array[i].data.mese, &array[i].data.anno, &array[i].mm_pioggia);
printf("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno, array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
else exit(1);
}
}
else exit(1);
fclose(file); }
You have two primary problems I see in crea_array,
You declare char* r; but then attempt to assign the return of sscanf (buf, "%s %d/%d/%d %d\n", ... (e.g. r = sscanf (.... This is incorrect. sscanf returns type int representing the number of successful conversions that took place as specified in your format string (e.g. "%s %d/%d/%d %d\n" would return 5 on success, and remove the '\n', it will cause problems). Your compiler should be screaming warnings at you. If not, you need to enable compiler warnings by adding -Wall -Wextra -pedantic as compiler options and do not accept code until it compiles without a single warning.
crea_array must be declared as type tipo_dati_file * and it must return array; at the end. You must assign the return to a pointer back in the caller. You must also free (buf); before the return or you have just created a memory leak as there is no way to free() the memory you allocated for buf after the function returns. (further, if you are simply allocating 1000-char each time, just use a fixed buffer, e.g. char buf[1000]; and eliminate the need to allocate buf completely.
Putting it altogether, you could do something similar to:
#define MAXC 1000 /* if you need a constant, #define one (or more) */
tipo_dati_file *crea_array (char* Nome_f, int v)
{
int i = 0,
s,
r;
char buf[MAXC] = "";
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file *array = malloc (v * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
exit (EXIT_FAILURE);
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", array[i].regione,
&array[i].data.giorno, &array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fput ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
printf ("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno,
array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
fclose (file);
return array;
}
Note: I have not compiled the code above.
Then in main, you could do something similar to:
tipo_dati_file *array = crea_array (name, v);
(note: you should also pass a 3rd parameter of type int *numelem so you can assign *numelem = i; before return making the number of elements filled available back in the caller if less than v are actually read)
If you will post a A Minimal, Complete, and Verifiable Example (MCVE), along with a sample data file (10 lines or so), I'm happy to help further and I can validate the code works -- as I will have something I can compile and run.
Edit Following Warnings Posted (in comments)
The two warnings are addressed in detail in the comments below the answer. Once those are resolved, you will run into a horrible SegFault as you are not allocating storage for array[i].regione.
In you nested set of structs:
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
regione is an uninitialized pointer that points to some indeterminate memory location (which you do not own). When you attempt to write characters there with sscanf (BOOM - SegFault -- most likely).
You have two choices (1) declare regione as a fixed array, e.g. char regione[CONST] (inefficient), or (2) read the string for regione into a temporary buffer and then allocate storage for strlen + 1 chars and copy the string from the temporary buffer to the new block of memory and assign the starting address for that block to regione (you can use strlen/malloc/memcpy or strdup -- if you have it, it does all three)
With that fix and a few tweaks to your crea_array function (like passing a pointer to int to hold the number of struct filled in array instead of v), you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXDATA 64
typedef enum mese_e { Genv= 1, Feb, Mar, Apr, Mag, Giu,
Lug, Ago, Set, Ott, Nov, Dic
} tipo_mese;
typedef struct data_s {
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count;
} tipo_ritorna;
tipo_dati_file *crea_array (char *Nome_f, int *nelem)
{
int i = 0,
r;
char region[MAXC] = ""; /* temp buffer to hold array[i].regione */
char buf[MAXC] = "";
FILE* file;
tipo_dati_file *array = malloc (MAXDATA * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
return NULL;
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
return NULL;
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", region,
&array[i].data.giorno, (int*)&array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fputs ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
array[i].regione = strdup (region);
if (!array[i].regione) { /* strdup allocates - you must validate */
perror ("strdup-array[i].regione");
for (int j = 0; j < i; j++) /* on failure free prior mem */
free (array[j].regione); /* and return NULL */
free (array);
return NULL;
}
i++;
}
fclose (file);
*nelem = i; /* update nelem with number of struct filled */
return array;
}
int main (int argc, char **argv) {
int index = 0,
nelem = 0;
char *datafile = argc > 1 ? argv[1] : "dat/staterain.txt";
tipo_ritorna statistics[MAXDATA] = {{ .array = NULL }};
statistics[index].array = crea_array (datafile, &nelem);
if (statistics[index].array && nelem > 0) {
statistics[index].count = nelem;
for (int i = 0; i < statistics[index].count; i++) {
printf ("%-12s %02d/%02d/%4d %3d\n",
statistics[index].array[i].regione,
statistics[index].array[i].data.giorno,
statistics[index].array[i].data.mese,
statistics[index].array[i].data.anno,
statistics[index].array[i].mm_pioggia);
free (statistics[index].array[i].regione); /* free strings */
}
free (statistics[index].array); /* free array */
}
return 0;
}
Example Use/Output
$ ./bin/staterain
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/staterain
==3349== Memcheck, a memory error detector
==3349== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3349== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3349== Command: ./bin/staterain
==3349==
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
==3349==
==3349== HEAP SUMMARY:
==3349== in use at exit: 0 bytes in 0 blocks
==3349== total heap usage: 5 allocs, 5 frees, 2,110 bytes allocated
==3349==
==3349== All heap blocks were freed -- no leaks are possible
==3349==
==3349== For counts of detected and suppressed errors, rerun with: -v
==3349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I am trying to write a simple program that will read words from a file and print the number of occurrences of a particular word passed to it as argument.
For that, I use fscanf to read the words and copy them into an array of strings that is dynamically allocated.
For some reason, I get an error message.
Here is the code for the readFile function:
void readFile(char** buffer, char** argv){
unsigned int i=0;
FILE* file;
file = fopen(argv[1], "r");
do{
buffer = realloc(buffer, sizeof(char*));
buffer[i] = malloc(46);
}while(fscanf(file, "%s", buffer[i++]));
fclose(file);
}
And here is the main function :
int main(int argc, char** argv){
char** buffer = NULL;
readFile(buffer, argv);
printf("%s\n", buffer[0]);
return 0;
}
I get the following error message :
realloc(): invalid next size
Aborted (core dumped)
I have looked at other threads on this topic but none of them seem to be of help. I could not apply whatever I learned there to my problem.
I used a debugger (VS Code with gdb). Data is written successfully into indices 0,1,2,3 of the buffer array but says error : Cannot access memory at address 0xfbad2488 for index 4 and pauses on exception.
Another thread on this topic suggests there might be a wild pointer somewhere. But I don't see one anywhere.
I have spent days trying to figure this out. Any help will be greatly appreciated.
Thanks.
Your algorithm is wrong on many fronts, including:
buffer is passed by-value. Any modifications where buffer = ... is the assignment will mean nothing to the caller. In C, arguments are always pass-by-value (arrays included, but their "value" is a conversion to temporary pointer to first element, so you get a by-ref synonym there whether you want it or not).
Your realloc usage is wrong. It should be expanding based on the iteration of the loop as a count multiplied by the size of a char *. You only have the latter, with no count multiplier. Therefore, you never allocate more than a single char * with that realloc call.
Your loop termination condition is wrong. Your fscanf call should check for the expected number of arguments to be processed, which in your case is 1. Instead, you're looking for any non-zero value, which EOF is going to be when you hit it. Therefore, the loop never terminates.
Your fscanf call is not protected from buffer overflow : You're allocating a static-sized string for each string read, but not limiting the %s format to the static size specified. This is a recipe for buffer-overflow.
No IO functions are ever checked for success/failure : The following APIs could fail, yet you never check that possibility: fopen, fscanf, realloc, malloc. In failing to do so, you're violating Henry Spencer's 6th Commandment for C Programmers : "If a function be advertised to return an error code in the event of difficulties, thou shalt check for that code, yea, even though the checks triple the size of thy code and produce aches in thy typing fingers, for if thou thinkest ``it cannot happen to me'', the gods shall surely punish thee for thy arrogance."
No mechanism for communicating the allocated string count to the caller : The caller of this function is expecting a resulting char**. Assuming you fix the first item in this list, you still have not provided the caller any means of knowing how long that pointer sequence is when readFile returns. An out-parameter and/or a formal structure is a possible solution to this. Or perhaps a terminating NULL pointer to indicate the list is finished.
(Moderate) You never check argc : Instead, you just send argv directly to readFile, and assume the file name will be at argv[1] and always be valid. Don't do that. readFile should take either a FILE* or a single const char * file name, and act accordingly. It would be considerably more robust.
(Minor) : Extra allocation : Even fixing the above items, you'll still leave one extra buffer allocation in your sequence; the one that failed to read. Not that it matter much in this case, as the caller has no idea how many strings were allocated in the first place (see previous item).
Shoring up all of the above would require a basic rewrite of nearly everything you have posted. In the end, the code would look so different, it's almost not worth trying to salvage what is here. Instead, look at what you have done, look at this list, and see where things went wrong. There's plenty to choose from.
Sample
#include <stdio.h>
#include <stdlib.h>
#define STR_MAX_LEN 46
char ** readFile(const char *fname)
{
char **strs = NULL;
int len = 0;
FILE *fp = fopen(fname, "r");
if (fp != NULL)
{
do
{
// array expansion
void *tmp = realloc(strs, (len+1) * sizeof *strs);
if (tmp == NULL)
{
// failed. cleanup prior success
perror("Failed to expand pointer array");
for (int i=0; i<len; ++i)
free(strs[i]);
free(strs);
strs = NULL;
break;
}
// allocation was good; save off new pointer
strs = tmp;
strs[len] = malloc( STR_MAX_LEN );
if (strs[len] == NULL)
{
// failed. cleanup prior sucess
perror("Failed to allocate string buffer");
for (int i=0; i<len; ++i)
free(strs[i]);
free(strs);
strs = NULL;
break;
}
if (fscanf(fp, "%45s", strs[len]) == 1)
{
++len;
}
else
{
// read failed. we're leaving regardless. the last
// allocation is thrown out, but we terminate the list
// with a NULL to indicate end-of-list to the caller
free(strs[len]);
strs[len] = NULL;
break;
}
} while (1);
fclose(fp);
}
return strs;
}
int main(int argc, char *argv[])
{
if (argc < 2)
exit(EXIT_FAILURE);
char **strs = readFile(argv[1]);
if (strs)
{
// enumerate and free in the same loop
for (char **pp = strs; *pp; ++pp)
{
puts(*pp);
free(*pp);
}
// free the now-defunct pointer array
free(strs);
}
return EXIT_SUCCESS;
}
Output (run against /usr/share/dict/words)
A
a
aa
aal
aalii
aam
Aani
aardvark
aardwolf
Aaron
Aaronic
Aaronical
Aaronite
Aaronitic
Aaru
Ab
aba
Ababdeh
Ababua
abac
abaca
......
zymotechny
zymotic
zymotically
zymotize
zymotoxic
zymurgy
Zyrenian
Zyrian
Zyryan
zythem
Zythia
zythum
Zyzomys
Zyzzogeton
Improvements
The secondary malloc in this code is completely pointless. You're using a fixed length word maximum size, so you could easily retool you array to be a pointer to use this:
char (*strs)[STR_MAX_LEN]
and simply eliminate the per-string malloc code entirely. That does leave the problem of how to tell the caller how many strings were allocated. In the prior version we used a NULL pointer to indicate end-of-list. In this version we can simply use a zero-length string. Doing this makes the declaration of readFile rather odd looking, but for returning a pointer-to-array-of-size-N, its' correct. See below:
#include <stdio.h>
#include <stdlib.h>
#define STR_MAX_LEN 46
char (*readFile(const char *fname))[STR_MAX_LEN]
{
char (*strs)[STR_MAX_LEN] = NULL;
int len = 0;
FILE *fp = fopen(fname, "r");
if (fp != NULL)
{
do
{
// array expansion
void *tmp = realloc(strs, (len+1) * sizeof *strs);
if (tmp == NULL)
{
// failed. cleanup prior success
perror("Failed to expand pointer array");
free(strs);
strs = NULL;
break;
}
// allocation was good; save off new pointer
strs = tmp;
if (fscanf(fp, "%45s", strs[len]) == 1)
{
++len;
}
else
{
// read failed. make the final string zero-length
strs[len][0] = 0;
break;
}
} while (1);
fclose(fp);
}
return strs;
}
int main(int argc, char *argv[])
{
if (argc < 2)
exit(EXIT_FAILURE);
char (*strs)[STR_MAX_LEN] = readFile(argv[1]);
if (strs)
{
// enumerate and free in the same loop
for (char (*s)[STR_MAX_LEN] = strs; (*s)[0]; ++s)
puts(*s);
free(strs);
}
return EXIT_SUCCESS;
}
The output is the same as before.
Another Improvement: Geometric Growth
With a few simple changes we can significantly cut down on the realloc invokes (we're currently doing one per string added) by only doing them in a double-size growth pattern. If each time we reallocate, we double the size of the prior allocation, we will make more and more space available for reading larger numbers of strings before the next allocation:
#include <stdio.h>
#include <stdlib.h>
#define STR_MAX_LEN 46
char (*readFile(const char *fname))[STR_MAX_LEN]
{
char (*strs)[STR_MAX_LEN] = NULL;
int len = 0;
int capacity = 0;
FILE *fp = fopen(fname, "r");
if (fp != NULL)
{
do
{
if (len == capacity)
{
printf("Expanding capacity to %d\n", (2 * capacity + 1));
void *tmp = realloc(strs, (2 * capacity + 1) * sizeof *strs);
if (tmp == NULL)
{
// failed. cleanup prior success
perror("Failed to expand string array");
free(strs);
strs = NULL;
break;
}
// save the new string pointer and capacity
strs = tmp;
capacity = 2 * capacity + 1;
}
if (fscanf(fp, "%45s", strs[len]) == 1)
{
++len;
}
else
{
// read failed. make the final string zero-length
strs[len][0] = 0;
break;
}
} while (1);
// shrink if needed. remember to retain the final empty string
if (strs && (len+1) < capacity)
{
printf("Shrinking capacity to %d\n", len);
void *tmp = realloc(strs, (len+1) * sizeof *strs);
if (tmp)
strs = tmp;
}
fclose(fp);
}
return strs;
}
int main(int argc, char *argv[])
{
if (argc < 2)
exit(EXIT_FAILURE);
char (*strs)[STR_MAX_LEN] = readFile(argv[1]);
if (strs)
{
// enumerate and free in the same loop
for (char (*s)[STR_MAX_LEN] = strs; (*s)[0]; ++s)
puts(*s);
// free the now-defunct pointer array
free(strs);
}
return EXIT_SUCCESS;
}
Output
The output is the same as before, but I added instrumentation to show when expansion happens to illustrate the expansions and final shrinking. I'll leave out the rest of the output (which is over 200k lines of words)
Expanding capacity to 1
Expanding capacity to 3
Expanding capacity to 7
Expanding capacity to 15
Expanding capacity to 31
Expanding capacity to 63
Expanding capacity to 127
Expanding capacity to 255
Expanding capacity to 511
Expanding capacity to 1023
Expanding capacity to 2047
Expanding capacity to 4095
Expanding capacity to 8191
Expanding capacity to 16383
Expanding capacity to 32767
Expanding capacity to 65535
Expanding capacity to 131071
Expanding capacity to 262143
Shrinking capacity to 235886
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.
I'm currently writing a method that reads from an allocated block of memory and prints out its contents from a certain offset and up to a specified size, both of which are passed as parameters. I'm using char pointers to accomplish this, but keep getting a malloc error around line
char *content = (char *)malloc(size+1);
Code for the method:
int file_read(char *name, int offset, int size)
{
//First find file and its inode, if existing
int nodeNum = search_cur_dir(name);
if(nodeNum < 0) {
printf("File read error: file does not exist\n");
return -1;
}
//Size check, to avoid overflows/overreads
if(offset > inode[nodeNum].size || size > inode[nodeNum].size || (offset+size) > inode[nodeNum].size) {
printf("File read error: offset and/or size is too large\n");
return -1;
}
int i, read_size, track_size = size, content_offset = 0;
int target_block = offset / BLOCK_SIZE; //Defined as constant 512
int target_index = offset % BLOCK_SIZE;
char *raw_content = (char *)malloc(inode[nodeNum].size+1);
printf("check1\n"); //Debug statment
for(i = target_block; i < (inode[nodeNum].blockCount-(size/BLOCK_SIZE)); i++) {
disk_read(inode[nodeNum].directBlock[i], raw_content+content_offset);
content_offset += BLOCK_SIZE;
}
printf("check2\n"); //Debug statment
char *content = (char *)malloc(size+1);
memcpy(content, raw_content+target_index, size);
printf("%s\n", content);
free(raw_content);
free(content);
return 0;
}
and code for disk_read:
char disk[MAX_BLOCK][BLOCK_SIZE]; //Defined as 4096 and 512, respectively
int disk_read(int block, char *buf)
{
if(block < 0 || block >= MAX_BLOCK) {
printf("disk_read error\n");
return -1;
}
memcpy(buf, disk[block], BLOCK_SIZE);
return 0;
}
structure for node
typedef struct {
TYPE type;
int owner;
int group;
struct timeval lastAccess;
struct timeval created;
int size;
int blockCount;
int directBlock[10];
int indirectBlock;
char padding[24];
} Inode; // 128 byte
The error I get when using this method is one of memory corruption
*** glibc detected *** ./fs_sim: malloc(): memory corruption (fast): 0x00000000009f1030 ***
Now the strange part is, firstly this only occurs after I have used the method a few times - for the first two or three attempts it will work and then the error occurs. For instance, here is an example test run:
% read new 0 5
z12qY
% read new 0 4
z12q
% read new 0 3
*** glibc detected *** ./fs_sim: malloc(): memory corruption (fast): 0x00000000009f1030 ***
Even stranger still, this error disappears completely when I comment out
free(raw_content);
free(content);
Even through this would tie up the memory. I've read through previous posts regarding malloc memory corruption and understand this usually results from overwriting memory bounds or under allocating space, but I can't see where I could be doing this. I've attempted other sizes for malloc as well and these produced the best results when I commented out the lines freeing both pointers. Does anyone see what I could be missing? And why does this occur so inconsistently?
Code allocates space for characters and a null character, but does not insure the array is terminated with a null character before printing as a string.
char *content = (char *)malloc(size+1);
memcpy(content, raw_content+target_index, size);
// add
content[size] = '\0';
printf("%s\n", content);
Likely other issues too.
[Edit]
OP code is prone to mis-coding and dependent on inode[] to have coherent values (.blockCount . size). Clarify and simplify by determining the loop count and allocating per that count.
int loop_count = (inode[nodeNum].blockCount-(size/BLOCK_SIZE)) - target_block;
char *raw_content = malloc(sizeof *raw_content * loop_count * BLOCK_SIZE);
assert(raw_count);
for (loop = 0; loop < loop_count; loop++) {
i = target_block + loop;
disk_read(inode[nodeNum].directBlock[i], raw_content + content_offset);
content_offset += BLOCK_SIZE;
}
Also recommend checking the success of disk_read()
While working on a program which requires frequent memory allocation I came across behaviour I cannot explain. I've implemented a work around but I am curious to why my previous implementation didn't work. Here's the situation:
Memory reallocation of a pointer works
This may not be best practice (and if so please let me knwow) but I recall that realloc can allocate new memory if the pointer passed in is NULL. Below is an example where I read file data into a temporary buffer, then allocate appropriate size for *data and memcopy content
I have a file structure like so
typedef struct _my_file {
int size;
char *data;
}
And the mem reallocation and copy code like so:
// cycle through decompressed file until end is reached
while ((read_size = gzread(fh, buf, sizeof(buf))) != 0 && read_size != -1) {
// allocate/reallocate memory to fit newly read buffer
if ((tmp_data = realloc(file->data, sizeof(char *)*(file->size+read_size))) == (char *)NULL) {
printf("Memory reallocation error for requested size %d.\n", file->size+read_size);
// if memory was previous allocated but realloc failed this time, free memory!
if (file->size > 0)
free(file->data);
return FH_REALLOC_ERROR;
}
// update pointer to potentially new address (man realloc)
file->data = tmp_data;
// copy data from temporary buffer
memcpy(file->data + file->size, buf, read_size);
// update total read file size
file->size += read_size;
}
Memory reallocation of pointer to pointer fails
However, here is where I'm confused. Using the same thought that reallocation of a NULL pointer will allocate new memory, I parse a string of arguments and for each argument I allocate a pointer to a pointer, then allocate a pointer that is pointed by that pointer to a pointer. Maybe code is easier to explain:
This is the structure:
typedef struct _arguments {
unsigned short int options; // options bitmap
char **regexes; // array of regexes
unsigned int nregexes; // number of regexes
char *logmatch; // log file match pattern
unsigned int limit; // log match limit
char *argv0; // executable name
} arguments;
And the memory allocation code:
int i = 0;
int len;
char **tmp;
while (strcmp(argv[i+regindex], "-logs") != 0) {
len = strlen(argv[i+regindex]);
if((tmp = realloc(args->regexes, sizeof(char **)*(i+1))) == (char **)NULL) {
printf("Cannot allocate memory for regex patterns array.\n");
return -1;
}
args->regexes = tmp;
tmp = NULL;
if((args->regexes[i] = (char *)malloc(sizeof(char *)*(len+1))) == (char *)NULL) {
printf("Cannot allocate memory for regex pattern.\n");
return -1;
}
strcpy(args->regexes[i], argv[i+regindex]);
i++;
}
When I compile and run this I get a run time error "realloc: invalid pointer "
I must be missing something obvious but after not accomplishing much trying to debug and searching for solutions online for 5 hours now, I just ran two loops, one counts the numbers of arguments and mallocs enough space for it, and the second loop allocates space for the arguments and strcpys it.
Any explanation to this behaviour is much appreciated! I really am curious to know why.
First fragment:
// cycle through decompressed file until end is reached
while (1) {
char **tmp_data;
read_size = gzread(fh, buf, sizeof buf);
if (read_size <= 0) break;
// allocate/reallocate memory to fit newly read buffer
tmp_data = realloc(file->data, (file->size+read_size) * sizeof *tmp_data );
if ( !tmp_data ) {
printf("Memory reallocation error for requested size %d.\n"
, file->size+read_size);
if (file->data) {
free(file->data)
file->data = NULL;
file->size = 0;
}
return FH_REALLOC_ERROR;
}
file->data = tmp_data;
// copy data from temporary buffer
memcpy(file->data + file->size, buf, read_size);
// update total read file size
file->size += read_size;
}
Second fragment:
unsigned i; // BTW this variable is already present as args->nregexes;
for(i =0; strcmp(argv[i+regindex], "-logs"); i++) {
char **tmp;
tmp = realloc(args->regexes, (i+1) * sizeof *tmp );
if (!tmp) {
printf("Cannot allocate memory for regex patterns array.\n");
return -1;
}
args->regexes = tmp;
args->regexes[i] = strdup( argv[i+regindex] );
if ( !args->regexes[i] ) {
printf("Cannot allocate memory for regex pattern.\n");
return -1;
}
...
return 0;
}
A few notes:
the syntax ptr = malloc ( CNT * sizeof *ptr); is more robust than the sizeof(type) variant.
strdup() does exactly the same as your malloc+strcpy()
the for(;;) loop is less error prone than a while() loop with a loose i++; at the end of the loop body. (it also makes clear that the loopcondition is never checked)
to me if ( !ptr ) {} is easyer to read than if (ptr != NULL) {}
the casts are not needed and sometimes unwanted.