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.
Related
--Important Edit--
Thanks for the tip on compiling with -fsanitize=address -g, it allowed me to track down the problem. I'm almost done and I've isolated the issue (which happens near the top of the cleanup function). To simplify things, why does the following (when compiled with the above flags) fail?
#include <stdio.h>
#include <stdlib.h>
struct pair {
char *left;
char *right;
};
int main() {
struct pair *pairs = malloc(100 * sizeof(*pairs));
for (int x = 0; x < 100; x++) {
printf("%i\n", x);
pairs->left = pairs->right = NULL;
pairs += sizeof(*pairs);
}
return 0;
}
After printing 0-7 on new lines, I get ==9803==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61b000000788 at pc 0x00010cb90d88 bp 0x7ffee306fa90 sp 0x7ffee306fa88...Address 0x61b000000788 is a wild pointer.
--Original Question--
I've been working on a brainfuck interpreter in C, but I keep inconsistently getting a segfault. While trying to debug this for a day, I've done many things which, rather than catching where the problem is, simply cause it not to happen. I think at this point I'm encountering undefined behavior, but after rereading my code multiple times I don't see where it could be happening. All of these things cause the program to work as intended:
Printing a variable amount of characters between the bottom of the function body of cleanup and the top of the function body of execute (including inside the main function), though this isn't always consistent
Compiling with the -g flag for debugging
At the top of the execute function
unsigned char *pointer = (unsigned char*) calloc(30000, 1);
unsigned char *leftbound = pointer, *rightbound = pointer;
rightbound += 29999;
changing 30000 to 1000 and 29999 to 999
I've read the documentation on malloc, realloc, and calloc, and browsed for other answers, and I still can't tell the problem. As far as I can tell, I have no memory leaks (even when I realloc a struct pair*, the memory at the pointers within each struct is not leaked because it is within the char *program block) or other issues. That's why I would provide the minimal answer to reproduce the problem, but I'm beginning to doubt that removing seemingly unrelated parts of my source code will have no effect on it (though I have stripped down my code a lot still).
I'm using Mac OS X 10.14, bash "gcc -o brainfc brainfc.c" OR "clang -o brainfc brainfc.c" to compile, "brainfc mandelbrot.b" to run program.
The mandelbrot.b file can be found here: http://esoteric.sange.fi/brainfuck/utils/mandelbrot/mandelbrot.b
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *program = NULL;
struct pair {
char *left;
char *right;
};
//Reads into global variable program from file
void fileinput(char *filename) {
FILE *fp;
fp = fopen(filename, "rb");
if (fp) {
size_t inputlen = 0;
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
rewind(fp);
program = malloc(filesize + 1);
fread(program, filesize, 1, fp);
*(program + filesize) = 0;
fclose(fp);
}
}
//Removes unwanted characters from program, as well as compiling lookup table of pairs
//This happens in a single sweep through the program for efficiency,
//though again this problem might not occur if I optimized for readability
struct pair* cleanup() {
int pairsize = 200;
struct pair *pairs = calloc(pairsize, sizeof(*pairs));
char *src, *dest;
struct pair *buildptr = pairs;
int bracketlevel = 0;
for (src = dest = program; *src; dest += (strchr("<>+-[].,", *src++) != NULL)) {
*dest = *src;
if (*dest == '[') {
bracketlevel++;
while (buildptr->left) {
if (buildptr == pairs + (pairsize - 1) * sizeof(*pairs)) {
pairsize += 100;
pairs = realloc(pairs, pairsize * sizeof(*pairs));
for (int x = 0; x < 100; x++) {
buildptr += sizeof(*pairs);
buildptr->left = buildptr->right = NULL;
}
buildptr -= sizeof(*pairs) * 100;
}
buildptr += sizeof(*pairs);
}
buildptr->left = dest;
} else if (*dest == ']') {
bracketlevel--;
if (bracketlevel < 0) {
return NULL;
}
while (buildptr->right) {
buildptr -= sizeof(*pairs);
}
buildptr->right = dest;
}
}
if (bracketlevel != 0) {
return NULL;
}
*dest = 0;
program = realloc(program, strlen(program) + 1);
return pairs;
}
//Executes program
int execute(struct pair *pairs) {
unsigned char *pointer = (unsigned char*) calloc(30000, 1);
unsigned char *leftbound = pointer, *rightbound = pointer;
rightbound += 29999;
for (char *pc = program; *pc; pc++) {
switch (*pc) {
case '<':
if (pointer == leftbound) return 1;
pointer--;
break;
case '>':
if (pointer == rightbound) return 1;
pointer++;
break;
case '+':
(*pointer)++;
break;
case '-':
(*pointer)--;
break;
case '[':
while (pairs->left != pc) pairs += sizeof(*pairs);
if (!(*pointer)) pc = pairs->right;
break;
case ']':
while (pairs->right != pc) pairs -= sizeof(*pairs);
if (*pointer) pc = pairs->left;
break;
case '.':
printf("%c", *pointer);
break;
case ',':
printf("Inputting 10 (for now)\n");
*pointer = 10;
break;
}
}
return 0;
}
//Parses command line arguments, calls each function in order
int main(int argc, char *argv[]) {
if (argc > 0) {
char *filepath = argv[1];
fileinput(filepath);
}
if (program == NULL) {
printf("Error: File not found\n");
return 3;
}
struct pair *pairs = cleanup();
if (pairs == NULL) {
printf("Error: Invalid program\n");
return 4;
}
int execstatus = execute(pairs);
switch (execstatus) {
case 1:
printf("\nError: Pointer out-of-bounds\n");
return 1;
case 2:
printf("\nError: Byte overflow\n");
return 2;
default:
return 0;
}
}
Any help would be greatly appreciated.
pairs += sizeof(*pairs);
Pointer arithmetic in C is always in units of the type pointed to - here, it's in units of struct pairs. So if you want pairs to point to the next struct pair in the array, add 1. (The compiler will internally translate this into adding the appropriate number of bytes, or however pointers happen to work on your system.) This line should be pairs += 1; or pairs++; or ++pairs; according to your taste.
As it stands, if sizeof(*pairs) happens to be, say, 16 on your system, you are skipping past 15 more struct pairs every time you iterate. You will end up accessing the 0th, 16th, 32nd, ... 1584th struct pair in the array. Since it only contains 100, obviously most of these will be out of bounds. Hence your segfault.
As previously mentioned the usage of pointers is a bit messed up.
Instead of
pairs->left = pairs->right = NULL;
pairs += sizeof(*pairs);
Use
pairs[x].left = pairs[x].right = NULL;
As a bonus you have pairs still intact to do the clean up
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.
I have the code below that fixes the IP addresses to 15 digit by padding 0 as prefix.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *function(char *newhost){
char *IPaddr;
IPaddr = (char *)calloc(16, sizeof(char));
size_t i=0 , j= 0;
for(i=0, j=0; j<15; i++, j++){
if((newhost[strlen(newhost)-(i+1)] == '.')){ //////////line 11
if( j == 3 || j == 7 || j == 11){
IPaddr[14-j] = '.';
}else if(j<3){
while(!(j==3)){
IPaddr[14-j]='0';
j++;
}
IPaddr[14-j] = '.';
}else if(j > 3 && j<7){
while(!(j==7)){
IPaddr[14-j]='0';
j++;
}
IPaddr[14-j] = '.';
}else if(j>7 && j<11){
while(!(j==11)){
IPaddr[14-j]='0';
j++;
}
IPaddr[14-j] = '.';
}
}else if(newhost[strlen(newhost)-(i+1)] == '\0'){ ///////////line33
while(!(j==15)){
IPaddr[14-j] = '0';
j++;
}
}else{
IPaddr[14-j] = newhost[strlen(newhost)-(i+1)];
}
}
printf("IPaddr: %s\n", IPaddr);
return IPaddr;
}
int main(int argc,char *argv[]){ /////////line48
char host[100] = {'\0'};
strcpy(host, "10.0.0.2");
char *new_IP;
new_IP = function(host); ////////////line52
printf("newIP:%s\n",new_IP);
free(new_IP);
return 0;
}
The code works and compiler outputs neither error nor warning. However, valgrind outputs (valgrind --tool=memcheck --leak-check=yes --track-origins=yes test
)
==22544== Conditional jump or move depends on uninitialised value(s)
==22544== at 0x8048547: function (test.c:11)
==22544== by 0x804872B: main (test.c:52)
==22544== Uninitialised value was created by a stack allocation
==22544== at 0x80486DB: main (test.c:48)
==22544==
==22544== Conditional jump or move depends on uninitialised value(s)
==22544== at 0x8048654: function (test.c:33)
==22544== by 0x804872B: main (test.c:52)
==22544== Uninitialised value was created by a stack allocation
==22544== at 0x80486DB: main (test.c:48)
Could anyone tell me how I can fix the code?
The trouble is that in the function, you are doing:
for (i=0, j=0; j < 15; i++, j++){
if ((newhost[strlen(newhost)-(i+1)] == '.')){
As you go through the loop, i becomes larger than the length of the initialized string, but I'd expect that to generate an 'out of bounds' error. However, because the variable in main() is not dynamically allocated, it may well be that valgrind can't help more. I recommend modifying main() to:
int main(int argc, char *argv[])
{
char *host = malloc(100);
if (host != 0)
{
strcpy(host, "10.0.0.2");
char *new_IP = function(host);
printf("newIP:%s\n", new_IP);
free(host);
free(new_IP);
}
return 0;
}
I expect valgrind to complain about more problems, in particular, out of bounds memory access.
Separately:
Since strlen(newhost) doesn't change while the function is running, you should compute it once, outside of the loop.
I'm not sure why you're using the if ((...)) notation. If it is a reflex action to avoid the compiler warning about using assignments in conditions, you're defeating the purpose of warning.
Would it be easier to parse the string into 4 numbers with sscanf() and then format them using sprintf()?
char *function(const char *newhost)
{
int o1, o2, o3, o4;
char *result = 0;
if (sscanf(newhost, "%d.%d.%d.%d", &o1, &o2, &o3, &o4) == 4)
{
/* Should check that values are in range 0..255 */
result = malloc(16);
if (result != 0)
sprintf(result, "%.3d.%.3d.%.3d.%.3d", o1, o2, o3, o4);
}
return result;
}
Another alternative implementation of your function(), using string copying only:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *function(const char *newhost)
{
char *result = malloc(16);
if (result != 0)
{
strcpy(result, "000.000.000.000");
const char *beg = newhost;
for (int i = 0; i < 4; i++)
{
const char *end = strchr(beg, '.');
if (end == 0)
end = beg + strlen(beg);
memcpy(result + (i * 4) + 3 - (end - beg), beg, end - beg);
beg = end + 1;
}
}
return result;
}
int main(void)
{
char host[] = "10.0.0.2";
char *newhost = function(host);
printf("%s => %s\n", host, newhost);
free(newhost);
return 0;
}
This calculates how many digits are in each segment, and then copies it to the right place in the result buffer which has already been filled with zeroes and dots at the right places. It avoids all the magic numbers like 3, 7, 11 that litter the original code.
I'd also recommend a different interface to function, avoiding dynamic memory allocation:
void function(const char *oldhost, char *newhost)
We can debate about void vs int, but the calling function should supply the (known, fixed size) buffer where the output is to be written, so as to avoid having to do dynamic memory allocation. You'd use int if the function does any validation on the string it is given; otherwise, it can be void. The existing code mostly does not report an error if passed an erroneous IP address.
gcc (GCC) 4.7.2
valgrind-3.8.1
c89
Hello,
==1160== Invalid read of size 1
==1160== at 0x8048C94: get_input_values (parse_cmd_input.c:278)
==1160== by 0x8048BA0: parse_input (parse_cmd_input.c:245)
==1160== by 0x80489A1: main (parse_cmd_input.c:50)
==1160== Address 0x40ef02c is 0 bytes after a block of size 4 alloc'd
==1160== at 0x40072C5: calloc (vg_replace_malloc.c:593)
==1160== by 0x8048B28: parse_input (parse_cmd_input.c:239)
==1160== by 0x80489A1: main (parse_cmd_input.c:50)
So its saying the address is reading a zero bytes of a allocated size of 4, and is trying to read 1 byte from it. However, I haven't over stepped the bounds of the array and I am accessing element 0.
I have checked with gdb, and element zero contains a character.
My program doesn't crash, and seems to work fine. But it might cause a problem on a production server.
I am not sure if I am correct here:
Should this be:
cpy_input = calloc(strlen(input) + 1, sizeof(char*));
or:
cpy_input = calloc(strlen(input) + 1, sizeof(char));
A char is 1 byte, and a pointer to a char is 4 bytes on my system.
The string passed in would be something like this "25 b"
int parse_input(const char *input)
{
char *cpy_input = NULL;
int has_error = -1;
if(strlen(input) == 0) {
LOG_ERR("FAILED: Empty string");
return -1;
}
cpy_input = calloc(strlen(input) + 1, sizeof(char));
apr_cpystrn(cpy_input, input, sizeof(cpy_input));
LOG_INFO("[ %s ]", cpy_input);
memset(&channel, 0, sizeof channel);
has_error = get_input_values(cpy_input, &channel);
free(cpy_input);
return has_error;
}
int get_input_values(const char *str, channel_t *channel)
{
size_t i = 0;
size_t k = 0;
int upper_flag = 0;
/* Indicates no digits or command found*/
channel->lower = -1;
channel->upper = -1;
channel->cmd = -1;
#define DIG_BUFFER_SIZE 32
char dig_buffer_lower[DIG_BUFFER_SIZE];
char dig_buffer_upper[DIG_BUFFER_SIZE];
if(strlen(str) == 0) {
LOG_ERR("FAILED: Empty string");
return -1;
}
memset(dig_buffer_lower, 0, DIG_BUFFER_SIZE);
memset(dig_buffer_upper, 0, DIG_BUFFER_SIZE);
LOG_INFO("SIZE %d %d", sizeof(char), sizeof(char*));
/* Increament and check for digits */
for(i = 0; i < DIG_BUFFER_SIZE; i++) {
switch(str[i]) {
case 32: /* ignore space */
continue;
case 45: /* '-' Start upper bounds */
LOG_DEBUG("Found a '-' check upper value");
/* Having a second '-' means the string is invalid */
if(!upper_flag) {
upper_flag = 1;
k = 0;
}
break;
} /* switch */
/* Insert on digits into the lower and upper values */
if(isdigit(str[i])) {
if(upper_flag) {
dig_buffer_upper[k++] = str[i];
LOG_DEBUG("dig_buffer_upper[%d] %s", i, dig_buffer_upper);
}
else {
/* Add to digit buffer */
dig_buffer_lower[k++] = str[i];
LOG_DEBUG("dig_buffer_lower[%d] %s", i, dig_buffer_lower);
}
}
} /* for loop */
Many thanks for any suggestions,
sizeof(cpy_input) is just sizeof(char *), and not the string length. Instead, say:
apr_cpystrn(cpy_input, input, strlen(input) + 1);
Or better, use a naked strcpy or equivalent. Also there's no need to zero out the array with calloc, since you're just about to overwrite it anyway. And since sizeof(char) is 1 by definition, you can allocate the array with:
cpy_input = malloc(strlen(input) + 1);
(Think about strings for a minute: You're already at the mercy of having a null terminator at a sensible place, or strlen will either crash or return a huge value. Once you trust the result of strlen, you are guaranteed to allocate enough memory to strcpy the string and the null terminator. Alternatively, you can use memcpy for a possibly even more efficient copy, since you know the size.)
Ok, maybe I'm missing something, but your for loop will iterate over 0 .. DIG_BUFFER_SIZE-1, reading from str[i]. I don't see what would cause that loop to break out early, especially since it seems to immediately wrap a switch, and so any break inside the switch would exit the switch, but not the for.
Your calloc(strlen(input) + 1, sizeof(char)); correctly allocates storage for the exact length of input. The code downstream in get_input_values doesn't seem to stop if the string is shorter than DIG_BUFFER_SIZE.
(I'd love to be proven wrong, but to know, we need to see more code.)
I'm working on a homework assignment and I need to basically create a character buffer. One of the functions I need to create is called "b_reset". It's purpose is to reinitialize the given buffer so that it will point to the first position in the char buffer. This is needed because later on, when a new char is added to the buffer, it needs to be added to the first position in the buffer.
This is the code I have thus far:
The struct:
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
The code:
int b_reset ( Buffer *pB )
{
Buffer *temp = NULL;
int i = 0;
int j = 1;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
temp = (Buffer*)malloc(sizeof(Buffer*));
if (temp == NULL)
{
return R_FAIL_1;
}
temp->ca_head = (char*)malloc(pB->capacity);
if (!temp->ca_head)
{
temp = NULL;
return R_FAIL_1;
}
for(i = 0;i < ca_getsize(pB);++i)
{
temp->ca_head[j] = pB->ca_head[i];
j++;
}
pB->ca_head = temp->ca_head;
//free(temp->ca_head);
//free(temp);
return 0;
}
}
My goal in this code was to create a temporary buffer that would basically shift over everything 1 time based on the actual given buffer. This would make the first position empty so another char could be added.
The problem I'm running into is that the original buffer doesn't seem to be returning the right values after I reset it.
When I do this for example:
temp->ca_head[0] = 'a';
temp->ca_head[1] = 'b';
temp->ca_head[2] = 'c';
temp->ca_head[3] = 'd';
temp->ca_head[4] = 'e';
b_reset(temp); //this will return the size as 0, when it's actually 5
//temp->ca_head[0] = 'i'; //if this is executed, it returns the size as 6
//and prints out the right values, but if it's not,
//it will not print out anything
printf("%d", ca_getsize(temp));
for(i = 0;i < ca_getsize(temp);++i)
{
printf("%c", temp->ca_head[i]);
}
I know something is going wrong here, but I'm not too sure what. Any suggestions would be greatly appreciated.
This code is based on your followup comment:
well I'm not trying to resize the buffer, I just want to create an
empty space in the first position, so basically shifting everything to
the right 1 time. The assumption is that there is a enough space in
the buffer to handle this process.
I don't think you need to do any malloc() ing beyond the initial one. You can just shift everything up in a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define R_FAIL_1 1
#define BUFFER_SIZE 10
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
void allocate_buffer(Buffer *pB, int size)
{
pB->ca_head = malloc(size);
assert(pB->ca_head);
pB->capacity = size;
}
int ca_getsize( Buffer *pB)
{
return pB->capacity;
}
int b_reset ( Buffer *pB )
{
int i = 0;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
if ( ca_getsize(pB) <= 0 || pB->ca_head == NULL )
return R_FAIL_1;
}
// shift data up by 1 byte
for( i = ca_getsize(pB) - 1 ; i > 0;i-- )
{
pB->ca_head[i] = pB->ca_head[i-1];
}
pB->ca_head[0] = '\0';
return 0;
}
void print_buffer(Buffer *pB)
{
printf("capacity: %d \n", ca_getsize(pB));
for (int i = 0;i < ca_getsize(pB);++i)
{
printf("buffer(%d): [%d] ",i, pB->ca_head[i]);
}
printf("\n");
}
int main(void)
{
Buffer a_buffer;
allocate_buffer(&a_buffer,BUFFER_SIZE);
strcpy(a_buffer.ca_head,"abcdefgh");
print_buffer(&a_buffer);
int ret = b_reset(&a_buffer);
assert(ret == 0);
print_buffer(&a_buffer);
}
temp = (Buffer*)malloc(sizeof(Buffer*));
You need to allocate enough space to hold a Buffer, but you only allocate enough space to hold a pointer to a buffer. This should be:
temp = (Buffer*)malloc(sizeof(Buffer));
You are managing your memory incorrectly. You are allocating memory for a new Buffer struct when actually you only need to handle the memory of the ca_head member (if my interpretation of your homework problem is correct).
Each time you invoke b_reset, you will allocate memory for this struct that will not be released. If you don't handle your memory correctly, you will experience unexpected results as the one you are reporting in your question.
I suggest you to make a research on the function realloc and use it properly in your b_reset function.
Good luck with your homework.