Why am I getting a segmentation fault? When I compile with sanitize=address I get a heap-use-after-free which I don't quite understand (the reason for). I get heap-use-after-free on address xyz.
Read of size 8 in ... test.c:22 (the line that prints parts[i])
... is located 0 bytes inside of a 8-byte region freed here (strings.c:175, which is the reallocarray line)
... previously allocated here(in main test.c:9, which is the char **parts=calloc.. line)
this example compiles standalone:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum EXPLODE_FLAGS {
NO_FLAGS=0,
FLAG_TRIM=1, // trim each line of the output
};
typedef enum RESULT {
E_SUCCESS=0, // not a failure but a SUCCESS
E_ERROR=1, // failure due to generic error
E_ARGS=2, // failed due to arguments
E_MALLOC=3
} RESULT;
enum EXP_RESULT {
EXP_ERROR=-E_ERROR, // generic error
EXP_ARGS=-E_ARGS, // generic error with the arguments
EXP_MALLOC=-E_MALLOC, // failure due to generic error
EXP_SEP=-4, // separator is null
EXP_INPUT=-5, // input is a null pointer
EXP_OUTPUT=-6, // output is a null pointer
};
int str_explode(char *input, char **parts, const char separator) {
int partCounter = 0;
int currentPartLength = 0;
char *currentPart = NULL;
// Check for input validity
if (!input) return EXP_INPUT;
if (!parts) return EXP_OUTPUT;
if (separator == '\0') return EXP_SEP;
char *start = input;
char *currentPartStart = input;
char *end = input + strlen(input);
fprintf(stdout,"Inside the function\n");
for (char *thischar = start; thischar <= end; thischar++) {
if (*thischar == separator || *thischar == '\0') {
printf("Inside check; current char is: %c\n",*thischar);
// Allocate memory for the length of the current part + null terminator
currentPart = calloc(1, currentPartLength + 1);
if (!currentPart) {
// Use goto for cleanup
goto cleanup;
}
// Copy the current part into the allocated memory
if (currentPartLength > 0) {
strncpy(currentPart, currentPartStart, currentPartLength);
currentPart[currentPartLength] = '\0'; // Null-terminate the string
} else {
currentPart[0] = '\0'; // Empty string for the case of consecutive separators
}
// Reallocate memory for another char pointer
parts=reallocarray(parts,partCounter+1,sizeof(char*));
if (!parts) {
// Use goto for cleanup
goto cleanup_current_part;
}
printf("About to add current part (%s) to the pile\n",currentPart);
// Add the new string part
parts[partCounter++] = currentPart;
printf("About to check current part from the pile: %s\n",parts[partCounter-1]);
// Reset variables for the next part
currentPart = NULL;
currentPartStart = thischar + 1; // Skip the separator
currentPartLength = 0;
if('\0'==*thischar)
break;
} else {
++currentPartLength;
}
}
free(currentPart);
return partCounter;
// Label for cleanup
cleanup_current_part:
fprintf(stderr,"Unable to allocate memory for another part\n");
free(currentPart);
cleanup:
fprintf(stderr,"Unable to allocate memory for current part\n");
// Free previously allocated memory before returning error
for (int i = 0; i < partCounter; i++) {
free(parts[i]);
}
free(parts);
return EXP_MALLOC;
}
int main(void) {
char *input = "apple;orange;banana;grape";
char **parts = calloc(1,1*sizeof(char*));
parts[0]="\0";
int partCount = str_explode(input, parts, ';');
if (partCount < 0) {
printf("Error code #%d\n", -partCount);
return 1;
}
printf("Original string: %s\n", input);
printf("Number of parts: %d\n", partCount);
for (int i = 0; i < partCount; i++) {
printf("About to print part #%d:\n",i+1);
printf("Part %d: %s\n", i + 1, parts[i]);
free(parts[i]);
}
free(parts);
return 0;
}
Please bear in mind that I am not an experienced C programmer. I have a working knowledge of pointers but I'm just not able to wrap my head around what I'm doing wrong here. The point of this little program is to improve my understanding of working with character arrays in C.
In C arguments are passed by value which means the changes to
partsitself are lost (and leaked) upon return ofstr_explode(). This also means you end up doing a double free of thepartsboth instr_explore()as part ofreallocarray()and the same (unchanged) variable inmain().The minimal fix is to pass in the address of the
partsand change the argument tochar ***partsand update all uses to be(*parts):and the resulting output:
This will get you labeled as a 3-star programmer. Two good redesigns would be to return the
partsand use a sentinelNULLto signify the there no more elements. Or create a struct to hold your two result values:which you can either return or use an an out parameter.
I suggest you initialize
char **parts = NULLand letstr_explode()allocate however many values you need. It's defect to free a constantparts[0] = "\0";...free(parts[i]). As you change it anyways, don't initialize it in the first place."\0"means{ '\0', '\0' }so I suggest either'\0'or""(but see above).parts=reallocarray(parts, ...)leaks the originalpartsifreallocarray()fails. Assign the result to temporary.