Two printfs print the same line in different ways

I am trying to create a library that handles large integer arithmetic. Large integers are stored in the structure:

typedef struct BigInt BigInt; struct BigInt { uint32_t size; uint32_t *data; }; 

the first member is uint32_t, which contains the length of the number, and the second element is a pointer indicating the actual data of the number (stored in two additions). I wrote a simple toHex function (BigInt * a) that allocates memory, prints the hexadecimal value of a large integer in a string, and returns the address.

In my main loop, I have the following:

 int main(int argc, char *argv[]) { char *ap, *bp; BigInt *a = fromUInt32(0x7fffffff), *b = fromUInt32(1), *c = fromUInt32(0x80000000); _add(a, b); ap = toHex(a); bp = toHex(c); printf("%s\n", ap); printf("%s\n%s\n", ap, bp); printf("%s\n%s\n", ap, bp); free(ap); free(bp); deleteBigInt(a); deleteBigInt(b); deleteBigInt(c); } 

what, oddly enough, prints

 0000000080000000 0 0000000080000000 0000000080000000 0000000080000000 

So in the second case, printf prints something different for ap than the first and third printf expression. The first printf statement seems to be correct, and the second is messy. I went through my code with GDB and after evaluating toHex, ap points to the line "0000000080000000", terminated by a null pointer.

I am completely puzzled. As far as I can see, the possibilities are:
1. I encountered undefined behavior for some strange reason.
2. In _add, I call a procedure written in x86 assembler, it may have an error (but I adhere to the GCC calling conventions, preserving esi, edi, ebx, ebp and esp).
3. There is an error in printf that seems very unlikely.

I also have an obvious “memory leak” (a quote due to the opinion that the memory leak seems to be different for sure) without freeing up the memory allocated toHex, but that doesn't matter.

The my toHex function was requested by Sourav Ghosh and looks like this:

 char numToHex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; char *toHex(BigInt *a) { char *result, *ptr; // allocate enough space for 8 characters for each uint32_t and 1 terminating 0 ptr = result = malloc(a->size * 8 + 1); // loop over the uint32_t stored in a->data // (there are a->size of them) for (uint32_t i = 0; i < a->size; i++) // parse 8 blocks of 4 bits for (uint32_t j = 0; j < 8; j++) // grab the right bits and convert them to a hex digit *(ptr++) = numToHex[(a->data[i] >> ((7 - j) * 4)) & 0xf]; // add a terminating zero byte *ptr = 0; return result; } 

I highlighted this strange behavior in the program ~ 100 lines of C + ~ 70 lines of assembly . Compilation can be done using

 nasm -f elf -s <AssemblyName>.asm gcc <CFile>.c <AssemblyName>.o -o <OutputProgram> -m32 -std=c99 -g 

The code is uncommented and designed for people who want to test the behavior for themselves.

EDIT: Ian Sterny and Matt McNabb encouraged me to use Walgrind. Valgrind says: Incorrect reading of size 1 at 0x40A5685: vfprintf (vfprintf.c: 1655) at 0x40AA7FE: printf (printf.c: 34) at 0x4075904: (below the main one) (libc-start.c: 260) Address 0x42121af - 1 byte up to a block of size 17 alloc'd on 0x40299D8: malloc (in / usr / lib / valgrind / vgpreload_memcheck-x86-linux.so) on 0x804887D: toHex (weird.c: 107) on 0x8048565: main (weird.c: 30)

But that doesn't make sense, as I set the result for malloc to toHex and didn't change anything after that. My bet now is that some records get corrupted in the build function. Edit2: After checking with GDB, I see that no registers are corrupted. I still don’t know.

+7
c gcc pointers linux i386
source share
2 answers

I made a thread on forum.osdev.org (there are really smart people there) and jnc100 notified me that the ABI was expecting a direction flag that should be cleared when calling functions. I set the direction flag in the build procedure (which is called in _add), and really cleaning up in the build procedure solves the problem.

+1
source share

The reduce function has an error:

 while (i < a->size && !(a->data[i])) i++; if (a->data[i] & SIGNBIT) i--; 

If the condition i < a->size fell, then a->data[i] goes beyond the limits, causing undefined behavior. Another reduce branch has the same problem


There is an error in the _add function (although this does not work in your test case):

 void *k = realloc(a->data, b->size * 4); memmove((void *)(a->data + displacement), (void *)a->data, a->size * 4); // ....other code using `a->data` 

After realloc , a->data becomes undefined, so it causes undefined behavior. This may explain your symptoms, as in the future the allocation may reuse the same freed block that a->data still points to.

Perhaps after this you should also have the line a->data = k; ?


To get good help when debugging your code, it would be great if you could do the following:

  • Check the result of all *alloc -family functions and exit if NULL returned. Otherwise, you will get undefined behavior (not reliable to expect segfault).
  • Rewrite the build function in C. This is a good idea for a number of reasons (debugging, code portability, optimization). It may turn out that gcc -O3 generates faster code than your handwritten version; that compilers are good.
  • Check the result of calling newAddress to see if it really returns what you expected in the test case.
+6
source share

All Articles