Segfault at srandom_r

1.2k views Asked by At

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

3

There are 3 answers

1
umläute On BEST ANSWER

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;
}
5
alk On

You might like ot add a test whether calloc() succeeded, as passing NULL most propably provokes the segmentation violation.

#include <stdlib.h>

...

  struct random_data * qq = calloc(50, sizeof(*qq));
  if (NULL == qq)
    perror("malloc() failed");
  else
    srandom_r(time(NULL), qq);
1
Secto Kia On

'random_data' does not contain the memory that holds the state of the random number generator - it holds the state the state is in....

The 'correct way' to use random_r is:

struct random_data rstate;
char random_bin[256];
initstate_r(MY_SEED,random_bin,256,&rstate);

Now you can call srandom_r and random_r.

There is no need to malloc or calloc anything, and doing so will slow you down as you will get cache misses everytime you go to generate a random number.