You are not logged in.

#1 2010-03-26 00:58:17

falconindy
Developer
From: New York, USA
Registered: 2009-10-22
Posts: 4,111
Website

[SOLVED][C] data corruption in 64 bit, but not in 32 bit?

This is driving me insane. I'm trying to splice in some code to Cower that parses dependencies out of a PKGBUILD. To do so, I've written a snippet such as this. It should be compiled on a 64 bit machine as:

gcc pbparse.c -g -Wall -pedantic -std=c99 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE  -lalpm -o pbp

You'll also need a PKGBUILD in the directory where you run the binary from. Preferrably, use a PKGBUILD that has at least 10 dependencies. chromium-browser-svn and mplayer-svn are two that I've been testing with.

As posted, the code works. The loop inside the subroutine correctly prints whatever is parsed out of the PKGBUILD and placed into the alpm_list_t. The loop in main dose the same. Here's where it gets strange. Comment out the for loop inside the subroutine, and the loop in main will print garbage after the 7th or 8th element in the array, but the loop will correctly terminate after the correct number of elements. However, compile the same code with the same gcc call on a 32 bit machine, and it works without a problem.

What Else I've Tried
* I replaced the alpm_list_t with a data structure of my own (happens to be a stack), and the behavior is the same.
* I can declare the data structure as a static global variable, and the behavior is the same.
* If I merge the subroutine into main, the problem goes away.

What the hell is going on?

Last edited by falconindy (2010-03-26 01:42:03)

Offline

#2 2010-03-26 01:10:03

Peasantoid
Member
Registered: 2009-04-26
Posts: 928
Website

Re: [SOLVED][C] data corruption in 64 bit, but not in 32 bit?

I had a similar problem with some C code a while back, except the situation was reversed: strings appeared fine on 64-bit, but were corrupted on 32-bit. After a bit of hunting, I discovered it was because it was because said strings had been allocated on the stack and subsequently overwritten. When the scope of a stack-allocated pointer/array exits, that variable is no longer, strictly speaking, guaranteed to point to a valid location. Or something along those lines. The point is that you can't always trust static memory allocation, and the way different architectures handle static memory is unpredictable.

If you want your arrays to definitely exist after the current scope exits, you should use heap (dynamic) allocation. For example, { return strdup("stuff"); } rather than { return "stuff"; }. You can *usually* do the latter if it's just one level of subscope, though - I don't know of any system that overwrites the stack variables after that point.

This is also why you should not return static arrays:

int *foo(void) {
    int n[] = {0, 1, 2, 3};
    return n; /* bad! */
}

In fact, GCC warns you if you compile the above.

In your snippet, I believe the issue is that you're doing alpm_list_add() with a statically-allocated string (I think strsep() does not do a strdup(), though I may be mistaken). Try

deplist = alpm_list_add(deplist, strdup(token));

instead of

deplist = alpm_list_add(deplist, token);

Note that global string variables do not have issues with stack clobbering, since they are guaranteed to exist for the process's entire lifetime.

Also, strdup()ed strings need to be free()ed manually, since strdup() uses malloc() internally.

Last edited by Peasantoid (2010-03-26 01:46:45)

Offline

#3 2010-03-26 01:41:52

falconindy
Developer
From: New York, USA
Registered: 2009-10-22
Posts: 4,111
Website

Re: [SOLVED][C] data corruption in 64 bit, but not in 32 bit?

Amazing. You're spot on. However, I'll point out that this leaves me with an awkward job of freeing the allocated memory from strdup. Your solution brings to light another possibility though, which I'm now using. The buffer I use to read in data from the file is indeed stack allocated. When I switch the buffer to a char* and use calloc/free, this works wonderfully.

I'd still like to understand why this worked on 32 bit and not 64 bit. The only explanation my feeble brain could come up with is something along the lines of: the stack is the same size in 32 bit and 64 bit, but pointers are double the length so old data is pushed off the stack sooner than in 32 bit.

edit: hmmm, but then my references within the calloc'd block aren't really valid once I free (valgrind yells at me when I read them). I wonder why I'm allowed to access them at all after that point........
edit to the edit: alpm_list_free_inner() to preface alpm_list_free() is my friend. RTFMFTWBBQ.

Last edited by falconindy (2010-03-26 05:15:47)

Offline

Board footer

Powered by FluxBB