Thread: Odd behavior in debugging segfaulting program

  1. #1
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139

    Odd behavior in debugging segfaulting program

    I have a program that attempts to create a binary lump file, which means it basically consists of metadata followed by raw, uncompressed file data Simply put, the header contains 8 pad bytes, a 4 byte LE value for the file count, a 4 byte LE value for the size (this is an old format meant for sizes < 4 GB), and then a series of file metadata entries consisting of a 4 byte LE value for the file offset, then a 32 character value denoting the file path.

    In an attempt to keep this portable and simplistic, I tried to stick to the standard library (libc, MSVCRT, etc), with an exception to a header from the C++ Boost library consisting solely of macros that determine machine endianness.

    It compiles fine and runs okay up to the point where the raw file data is output, which triggers a segfault. Naturally, I found this out by debugging. However, something happened that baffles me in regards to what GDB was showing. I commented out the for-loop that inserts the file data in order to assess if at least the metadata was coming out okay.

    It still segfaulted, so I went into GDB again, breakpointed the suspect function, ran it, and began stepping through the function line-by-line. The commented out for-loop was still in the program code!

    I tried resaving the code file, deleting the output executable and object files, and even stripped out the commented out for-loop entirely. All three attempts yielded the same for-loop I supposedly commented out.

    I really hope someone can spot something in my code that would cause this. I'd hate to be faced with the possibility of this being an internal compiler bug. GCC is needed to build, because this uses its __builtin_bswapXX functions.

    Code:
    #include <stdint.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <boost/detail/endian.hpp>
    
    #define PAC_INIT_LEN 16
    #define PAC_PATH_LEN 32
    #define PAC_ITEM_META_LEN 36
    
    struct _pac_item {
    	uint8_t *buffer;
    	char path[PAC_PATH_LEN];
    	uint32_t offset;
    };
    
    typedef struct _pac {
    	struct _pac_item *items;
    	uint32_t count;
    	uint32_t size;
    } pac_t;
    
    pac_t* pac_new() {
    	pac_t *p = (pac_t *)malloc(sizeof(pac_t));
    	p->count = 0;
    	p->size = PAC_INIT_LEN;
    	p->items = NULL;
    	return p;
    }
    
    void pac_info(const pac_t *p) {
    	if (p) {
    		printf("Files: %u (%X)\n", p->count, p->count);
    		printf("Size: %u (%X)\n", p->size, p->size);
    		
    		if (p->items) {
    			printf("File offsets: ");
    			for (int i = 0; i < p->count; i++)
    				printf("%u (%X) ", p->items[i].offset, p->items[i].offset);
    			printf("\nFile paths: ");
    			for (int i = 0; i < p->count; i++)
    				printf("%s ", p->items[i].path);
    			printf("\n");
    		}
    	} else
    		printf("PAC not initialised\n");
    }
    
    void pac_add(pac_t *p, char *fp) {
    	if (p) {
    		struct stat *si = (struct stat *)malloc(sizeof(struct stat));
    		stat(fp, si);
    		p->size += PAC_ITEM_META_LEN;
    		p->count++;
    		
    		if (p->items)
    			p->items = (struct _pac_item *)realloc(p->items, sizeof(struct _pac_item) * p->count);
    		else
    			p->items = (struct _pac_item *)malloc(sizeof(struct _pac_item) * p->count);
    		
    		p->items[p->count-1].offset = p->size;
    
    #ifdef _WIN32
    		for (int i = 0; i < PAC_PATH_LEN; i++)
    			if (fp[i] == '\\')
    				fp[i] = '/';
    #endif // _WIN32
    
    		if (strlen(fp) <= PAC_PATH_LEN) {
    			memset(p->items[p->count-1].path, 0, PAC_PATH_LEN);
    			strncpy(p->items[p->count-1].path, fp, PAC_PATH_LEN);
    		}
    
    		FILE * f = fopen(fp, "r+b");
    		p->items[p->count-1].buffer = (uint8_t *)malloc(si->st_size);
    		fread((void *)p->items[p->count-1].buffer, 1, si->st_size, f);
    		fclose(f);
    
    		p->size += si->st_size;
    		free(si);
    	}
    }
    
    uint8_t* pac_to_buffer(const pac_t *p) {
    	if (p) {
    		uint8_t *buf = (uint8_t *)malloc(p->size);
    		uint8_t *pos = buf;
    #ifdef BOOST_BIG_ENDIAN
    		uint32_t count_le = __builtin_bswap32(p->count);
    		uint32_t size_le = __builtin_bswap32(p->size);
    #else
    		uint32_t count_le = p->count;
    		uint32_t size_le = p->size;
    #endif // BOOST_BIG_ENDIAN
    		uint32_t offset_le;
    		
    		memset((void *)buf, 0, 8);
    		pos += 8;
    
    		memcpy((void *)pos, &count_le, 4);
    		pos += 4;
    		
    		memcpy((void *)pos, &size_le, 4);
    		pos += 4;
    		
    		for (int i = 0; i < p->count; i++) {
    #ifdef BOOST_BIG_ENDIAN
    			offset_le = __builtin_bswap32(p->items[i].offset);
    #else
    			offset_le = p->items[i].offset;
    #endif // BOOST_BIG_ENDIAN
    			memcpy((void *)buf, &offset_le, 4);
    			memcpy((void *)buf, (void *)p->items[i].path, PAC_PATH_LEN);
    		}
    		
    		/*for (int i = 0; i < p->count; i++) {
    			uint32_t bufsize = (i == p->count-1 ? (p->size - p->items[i].offset) : (p->items[i+1].offset - p->items[i].offset));
    			memcpy((void *)buf, p->items[i].buffer, bufsize);
    		}*/
    	} else
    		return NULL;
    }
    
    void pac_free(pac_t *p) {
    	if (p) {
    		for (int i = 0; i < p->count; i++)
    			free(p->items[i].buffer);
    		free(p->items);
    		free(p);
    	}
    }
    
    int main() {
    	FILE *f = fopen("test.pac", "w+b");
    	pac_t *p = pac_new();
    	pac_add(p, "L9jUC1T.png");
    	pac_info(p);
    	fwrite(pac_to_buffer(p), 1, p->size, f);
    	pac_free(p);
    	fclose(f);
    	return 0;
    }
    Last edited by Chris87; 11-01-2013 at 11:23 PM.

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    why you are using C++ header in the C program is beyond me

    You do not have enough error checking - so if malloc or fopen fail - you continue as if everything is fine - this is the trip to segfaults

    lines 57-60 you do not need if - realloc with NULL first parameter will work as malloc

    line 113-114 - looks like you always overwrite beginning of the buffer - should not it be pos with advancing pos instead?

    Why in the pac_add you allocate and free si instead of just creating automatic var - I do not understand as well


    The described symptoms could be caused by buffer overrun with malloced buffer - but I have not found such problem just by looking at the code - I suggest to use valgrind
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    Registered User Chris87's Avatar
    Join Date
    Dec 2007
    Posts
    139
    Wow... I had one heck of a brainfart. Thanks for the point-outs.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Segfaulting - don't know why
    By Yinyang107 in forum C Programming
    Replies: 4
    Last Post: 11-21-2010, 06:44 PM
  2. Very strange behavior from very simple program
    By somekid413 in forum C Programming
    Replies: 5
    Last Post: 12-17-2009, 08:53 PM
  3. segfaulting!
    By CMakesMeSad :( in forum C Programming
    Replies: 23
    Last Post: 07-10-2009, 01:04 PM
  4. Segfaulting Distance Program
    By radiohead in forum C Programming
    Replies: 2
    Last Post: 01-09-2006, 08:48 PM
  5. Why is it segfaulting?
    By XSquared in forum C Programming
    Replies: 7
    Last Post: 03-30-2004, 06:52 AM