Home > Back-end >  How to get rid of nondeterminism in C . Tried ADDR_NO_RANDOMIZE, other things
How to get rid of nondeterminism in C . Tried ADDR_NO_RANDOMIZE, other things

Time:02-19

I have a C program that is nondeterministic. I run it with an input file, and it runs ok. I run it with the same input file a second time, and it crashes. I'd like to get rid of the nondeterminism in order to make crashes reproducible.

I threw in some print-statements to print the addresses of certain data structures. With each execution, the data structures are at different addresses (under Linux).

One obvious reason that malloc would return unpredictable addresses would be ASLR. I turned that off. I can verify that it's off - the shared libraries are always loaded at the same addresses, the stack is always at the same address, and so forth. But even with ASLR off, malloc still isn't reproducible - it returns different addresses on different successive runs.

I'm wracking my brain to find possible sources of nondeterminism:

  • I've run the program with 'strace'. I can 'diff' the straces from two successive executions, and there are no diffs except the print-statements that print the addresses of my data structures (which are at different addresses).

  • It's not using threads, to my knowledge, unless glibc or C is using threads behind the scenes. I do notice that ptmalloc uses __thread variables... could this be relevant?

  • No signal handlers other than the default ones. I'm not using signals intentionally.

  • Theoretically, it would be possible for something in glibc to get one of the CPU performance counters, and use that as a source of nondeterminism. But I'm skeptical that's what's happening.

Does anybody know what could be the cause of malloc returning different addresses on successive executions?

UPDATE:

Here's the smallest program I have found that exhibits nondeterminism, even with ASLR turned off:


int main(int argc, char **argv) {
    // Turn off ASLR address space layout randomization.
    const int old_personality = personality(ADDR_NO_RANDOMIZE);
    if (!(old_personality & ADDR_NO_RANDOMIZE)) {
       const int new_personality = personality(ADDR_NO_RANDOMIZE);
       if (new_personality & ADDR_NO_RANDOMIZE) {
           execv(argv[0], argv);
       }
    }
    
    // Create a lua engine, then free it.
    lua_State *L = luaL_newstate();
    lua_close(L);

    // Allocate a big block of RAM.
    malloc(4*1024*1024);
    
    // Now print the hash of some mallocs.
    int hash = 0;
    for (int i = 0; i < 100; i  ) {
        int n = (int)(ptrdiff_t)malloc(1);
        hash = (hash * 17)   n;
    }
    fprintf(stderr, "x\n", hash);
}

Here's the output from three runs:

$ ./foo
c75ba620
$ ./foo
0e2e5210
$ ./foo
7c38ba10

I have no idea why the lua allocation is relevant, but it doesn't do it without the luaL_newstate and lua_close. It also doesn't do it without the 4-megabyte malloc in the middle.

UPDATE 2:

I found the source of nondeterminism. The lua library is calling time(0) to obtain the current time, and then it's using that as a random seed that affects what memory allocations it makes. The reason it took so long to find this is that 'strace' isn't reporting the syscall to 'time(0).' I had assumed that all system calls were reported by strace.

CodePudding user response:

Does anybody know what could be the cause of malloc returning different addresses on successive executions?

Assuming the process is actually single-threaded and no block address randomization is done by malloc, I guess:

Your program may have allocated random amount of memory at some point, for example, by using unreliable garbage value when calculating the size of some block to allocate. Addresses returned by all subsequent allocations might be affected (and thus randomized) by that.

Of course, that will depend on malloc implementation and actual size of allocation: if it is implemented as a per-thread bucket allocator, and the random block size doesn't exceed the bucket size, then the impact on subsequent allocations would be next to none.

CodePudding user response:

In this case, the nondeterminism was coming from inside the Lua runtime. Lua is using 'time(0)' to generate a random seed, which then affects what malloc calls are made by Lua.

The reason that this source of nondeterminism was hidden from me is that linux 'strace' doesn't report the system call to "time(0)". I had assumed that strace would show me any system calls that returned different values on successive executions.

CodePudding user response:

You are propably victim of memory leak problem. Your program use memory which is not allocated by you. If this memory address is in the same memory block in which you have something allocated program does not crash. But if program accesses memory in block you have not get right to read/write, operation system kills your program - you see this as a crash.

  • Related