Search code examples
clinuxrandomreentrancy

Segfault at srandom_r


I'm at my wits end. This trivial code gives me Segfault, what could be possibly wrong??

struct  random_data *qq;

qq = calloc(50, sizeof(struct random_data));
srandom_r(time(NULL), qq);

Now if I change it like this, it works:

struct  random_data qq;

srandom_r(time(NULL), &qq);

I must be a total moron, but I can't grasp it. Please, help.

Update: calloc returns a valid pointer

(uint64_t) 1aa5010

However, &qq represents the pointer

(uint64_t) 7fffbb428090

and that's the difference, but it's unclear why srandom_r fails to execute. I try at Linux 2.6.32-44-server #98-Ubuntu


Solution

  • it seems like most answers never actually tried to run your code. here's a very minimalistic program, that indeed exhibits your problem:

    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    int main() {
     int seed = time(NULL);
     struct random_data *qq = NULL;
    
     qq = calloc(1, sizeof(struct random_data));
     if(qq) {
       srandom_r(seed, qq); /* segfault! */
     } else {
       printf("failed to allocate `qq`\n");
     }
     return 0;
    }
    

    valgrind doesn't show much apart from an illegal memory access:

    ==22907== Invalid write of size 4 ==22907== at 0x409CE8D: srandom_r (random_r.c:182) ==22907== by 0x80484A1: main (srand_ko.c:10) ==22907== Address 0x0 is not stack'd, malloc'd or (recently) free'd

    now when looking at the random_data struct, you will find that it contains a pointer to a state-buffer:

    struct random_data
      {
        int32_t *fptr;              /* Front pointer.  */
        int32_t *rptr;              /* Rear pointer.  */
        int32_t *state;             /* Array of state values.  */
        int rand_type;              /* Type of random number generator.  */
        int rand_deg;               /* Degree of random number generator.  */
        int rand_sep;               /* Distance between front and rear.  */
        int32_t *end_ptr;           /* Pointer behind state table.  */
      };
    

    obviously all those pointers will be NULL if you allocate with calloc(), and srandom_r doesn't really like that. you can help it be manually allocating an array of state values and assign it to the random_data struct using initstate_r. since initstate_r already takes a seed, you don't need to call srandom_r anymore (but you can if you want to):

    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    #define STATESIZE 64
    int main() {
     int seed = time(NULL);
     char*buf = (char*)calloc(STATESIZE, sizeof(char));
     struct random_data *qq = NULL;
    
     qq = calloc(1, sizeof(struct random_data));
     initstate_r(seed, buf, STATESIZE, qq);
     /* generate some random numbers */
     /* ... */
    
     srandom_r(seed, qq);
     /* generate the same random numbers again */
     /* ... */
    
     /* cleanup */
     free(buf);
     free(qq);
     return 0;
    }