diff mbox series

toplev: read from /dev/urandom only when needed

Message ID alpine.LNX.2.20.13.1709202325530.28329@monopod.intra.ispras.ru
State New
Headers show
Series toplev: read from /dev/urandom only when needed | expand

Commit Message

Alexander Monakov Sept. 20, 2017, 8:39 p.m. UTC
Hi,

Most compiler invocations don't actually need an entropy source, so
open-read-close syscall sequence on /dev/urandom that GCC performs on
each startup is useless (and can easily be avoided).

This patch makes GCC read entropy from /dev/urandom lazily on first
call to get_random_seed, and en passant cleans up some of the cruft.

Tested on x86-64, OK to apply?

Thanks.
Alexander

	* toplev.h (set_random_seed): Adjust return type.
	* toplev.c (init_local_tick): Move eager initialization of random_seed
	to get_random_seed.  Adjust comment.
	(init_random_seed): Inline to get_random_seed, delete.
	(get_random_seed): Initialize random_seed lazily.
	(set_random_seed): Do not return previous value.
	(print_switch_value): Do not call get_random_seed.

Comments

Richard Biener Sept. 21, 2017, 7:47 a.m. UTC | #1
On Wed, Sep 20, 2017 at 10:39 PM, Alexander Monakov <amonakov@ispras.ru> wrote:
> Hi,
>
> Most compiler invocations don't actually need an entropy source, so
> open-read-close syscall sequence on /dev/urandom that GCC performs on
> each startup is useless (and can easily be avoided).
>
> This patch makes GCC read entropy from /dev/urandom lazily on first
> call to get_random_seed, and en passant cleans up some of the cruft.
>
> Tested on x86-64, OK to apply?

Ok.

Thanks,
Richard.

> Thanks.
> Alexander
>
>         * toplev.h (set_random_seed): Adjust return type.
>         * toplev.c (init_local_tick): Move eager initialization of random_seed
>         to get_random_seed.  Adjust comment.
>         (init_random_seed): Inline to get_random_seed, delete.
>         (get_random_seed): Initialize random_seed lazily.
>         (set_random_seed): Do not return previous value.
>         (print_switch_value): Do not call get_random_seed.
>
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index 7d2b8fffa0b..da2d7c566b1 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -239,7 +239,7 @@ announce_function (tree decl)
>      }
>  }
>
> -/* Initialize local_tick with a random number or -1 if
> +/* Initialize local_tick with the time of day, or -1 if
>     flag_random_seed is set.  */
>
>  static void
> @@ -247,19 +247,6 @@ init_local_tick (void)
>  {
>    if (!flag_random_seed)
>      {
> -      /* Try urandom first. Time of day is too likely to collide.
> -        In case of any error we just use the local tick. */
> -
> -      int fd = open ("/dev/urandom", O_RDONLY);
> -      if (fd >= 0)
> -        {
> -          if (read (fd, &random_seed, sizeof (random_seed))
> -              != sizeof (random_seed))
> -            random_seed = 0;
> -          close (fd);
> -        }
> -
> -      /* Now get the tick anyways  */
>  #ifdef HAVE_GETTIMEOFDAY
>        {
>         struct timeval tv;
> @@ -280,34 +267,33 @@ init_local_tick (void)
>      local_tick = -1;
>  }
>
> -/* Set up a default flag_random_seed and local_tick, unless the user
> -   already specified one.  Must be called after init_local_tick.  */
> -
> -static void
> -init_random_seed (void)
> -{
> -  if (!random_seed)
> -    random_seed = local_tick ^ getpid ();  /* Old racey fallback method */
> -}
> -
>  /* Obtain the random_seed.  Unless NOINIT, initialize it if
>     it's not provided in the command line.  */
>
>  HOST_WIDE_INT
>  get_random_seed (bool noinit)
>  {
> -  if (!flag_random_seed && !noinit)
> -    init_random_seed ();
> +  if (!random_seed && !noinit)
> +    {
> +      int fd = open ("/dev/urandom", O_RDONLY);
> +      if (fd >= 0)
> +        {
> +          if (read (fd, &random_seed, sizeof (random_seed))
> +              != sizeof (random_seed))
> +            random_seed = 0;
> +          close (fd);
> +        }
> +      if (!random_seed)
> +       random_seed = local_tick ^ getpid ();
> +    }
>    return random_seed;
>  }
>
> -/* Modify the random_seed string to VAL.  Return its previous
> -   value.  */
> +/* Set flag_random_seed to VAL, and if non-null, reinitialize random_seed.  */
>
> -const char *
> +void
>  set_random_seed (const char *val)
>  {
> -  const char *old = flag_random_seed;
>    flag_random_seed = val;
>    if (flag_random_seed)
>      {
> @@ -318,7 +304,6 @@ set_random_seed (const char *val)
>        if (!(endp > flag_random_seed && *endp == 0))
>          random_seed = crc32_string (0, flag_random_seed);
>      }
> -  return old;
>  }
>
>  /* Handler for fatal signals, such as SIGSEGV.  These are transformed
> @@ -818,11 +803,6 @@ print_switch_values (print_switch_fn_type print_fn)
>    int pos = 0;
>    size_t j;
>
> -  /* Fill in the -frandom-seed option, if the user didn't pass it, so
> -     that it can be printed below.  This helps reproducibility.  */
> -  if (!flag_random_seed)
> -    init_random_seed ();
> -
>    /* Print the options as passed.  */
>    pos = print_single_switch (print_fn, pos,
>                              SWITCH_TYPE_DESCRIPTIVE, _("options passed: "));
> diff --git a/gcc/toplev.h b/gcc/toplev.h
> index 2f6b587d233..aed806eb28f 100644
> --- a/gcc/toplev.h
> +++ b/gcc/toplev.h
> @@ -91,7 +91,7 @@ extern bool set_src_pwd                      (const char *);
>  /* Functions used to manipulate the random seed.  */
>
>  extern HOST_WIDE_INT get_random_seed (bool);
> -extern const char *set_random_seed (const char *);
> +extern void set_random_seed (const char *);
>
>  extern void initialize_rtl (void);
>
Jakub Jelinek Sept. 21, 2017, 1:36 p.m. UTC | #2
On Wed, Sep 20, 2017 at 11:39:26PM +0300, Alexander Monakov wrote:
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -239,7 +239,7 @@ announce_function (tree decl)
>      }
>  }
>  
> -/* Initialize local_tick with a random number or -1 if
> +/* Initialize local_tick with the time of day, or -1 if
>     flag_random_seed is set.  */
>  
>  static void
> @@ -247,19 +247,6 @@ init_local_tick (void)
>  {
>    if (!flag_random_seed)
>      {
> -      /* Try urandom first. Time of day is too likely to collide. 
> -	 In case of any error we just use the local tick. */
> -
> -      int fd = open ("/dev/urandom", O_RDONLY);
> -      if (fd >= 0)
> -        {
> -          if (read (fd, &random_seed, sizeof (random_seed))
> -              != sizeof (random_seed))
> -            random_seed = 0;
> -          close (fd);
> -        }
> -
> -      /* Now get the tick anyways  */
>  #ifdef HAVE_GETTIMEOFDAY
>        {
>  	struct timeval tv;
> @@ -280,34 +267,33 @@ init_local_tick (void)
>      local_tick = -1;
>  }

Why isn't init_local_tick done at the get_random_seed time too?
I.e. inlined into get_random_seed by hand like you've done for
init_random_seed?

>  /* Obtain the random_seed.  Unless NOINIT, initialize it if
>     it's not provided in the command line.  */
>  
>  HOST_WIDE_INT
>  get_random_seed (bool noinit)
>  {
> -  if (!flag_random_seed && !noinit)
> -    init_random_seed ();
> +  if (!random_seed && !noinit)
> +    {
> +      int fd = open ("/dev/urandom", O_RDONLY);
> +      if (fd >= 0)
> +        {
> +          if (read (fd, &random_seed, sizeof (random_seed))
> +              != sizeof (random_seed))
> +            random_seed = 0;
> +          close (fd);
> +        }
> +      if (!random_seed)
> +	random_seed = local_tick ^ getpid ();

If one is unlucky, this can still yield 0 and multiple get_random_seed
invocations will open /dev/urandom multiple times and what is worse,
one can return 0 and the other some completely different number.
So either we should for the case the above is still 0 set random_seed
to some hardcoded constant other than 0 or return getpid () in that case,
or allow 0 to be returned, but arrange for the initialization to be done
at most once.

	Jakub
Alexander Monakov Sept. 21, 2017, 2:09 p.m. UTC | #3
On Thu, 21 Sep 2017, Jakub Jelinek wrote:
> Why isn't init_local_tick done at the get_random_seed time too?
> I.e. inlined into get_random_seed by hand like you've done for
> init_random_seed?

init_local_tick initializes the 'local_tick' global variable that
is directly accessed from coverage.c.  Thus, changes in coverage.c
would be needed to avoid init_local_tick at each startup.

> If one is unlucky, this can still yield 0 and multiple get_random_seed
> invocations will open /dev/urandom multiple times and what is worse,
> one can return 0 and the other some completely different number.
> So either we should for the case the above is still 0 set random_seed
> to some hardcoded constant other than 0 or return getpid () in that case,
> or allow 0 to be returned, but arrange for the initialization to be done
> at most once.

Yes (there are pre-existing issues like this too).  The minimal fix could be

  random_seed += !random_seed;

after the getpid fallback, but really this internal random_seed business is
currently a mess and could benefit from some more TLC.  Would you be interested
to review further patches in this area?

Thanks.
Alexander
diff mbox series

Patch

diff --git a/gcc/toplev.c b/gcc/toplev.c
index 7d2b8fffa0b..da2d7c566b1 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -239,7 +239,7 @@  announce_function (tree decl)
     }
 }
 
-/* Initialize local_tick with a random number or -1 if
+/* Initialize local_tick with the time of day, or -1 if
    flag_random_seed is set.  */
 
 static void
@@ -247,19 +247,6 @@  init_local_tick (void)
 {
   if (!flag_random_seed)
     {
-      /* Try urandom first. Time of day is too likely to collide. 
-	 In case of any error we just use the local tick. */
-
-      int fd = open ("/dev/urandom", O_RDONLY);
-      if (fd >= 0)
-        {
-          if (read (fd, &random_seed, sizeof (random_seed))
-              != sizeof (random_seed))
-            random_seed = 0;
-          close (fd);
-        }
-
-      /* Now get the tick anyways  */
 #ifdef HAVE_GETTIMEOFDAY
       {
 	struct timeval tv;
@@ -280,34 +267,33 @@  init_local_tick (void)
     local_tick = -1;
 }
 
-/* Set up a default flag_random_seed and local_tick, unless the user
-   already specified one.  Must be called after init_local_tick.  */
-
-static void
-init_random_seed (void)
-{
-  if (!random_seed)
-    random_seed = local_tick ^ getpid ();  /* Old racey fallback method */
-}
-
 /* Obtain the random_seed.  Unless NOINIT, initialize it if
    it's not provided in the command line.  */
 
 HOST_WIDE_INT
 get_random_seed (bool noinit)
 {
-  if (!flag_random_seed && !noinit)
-    init_random_seed ();
+  if (!random_seed && !noinit)
+    {
+      int fd = open ("/dev/urandom", O_RDONLY);
+      if (fd >= 0)
+        {
+          if (read (fd, &random_seed, sizeof (random_seed))
+              != sizeof (random_seed))
+            random_seed = 0;
+          close (fd);
+        }
+      if (!random_seed)
+	random_seed = local_tick ^ getpid ();
+    }
   return random_seed;
 }
 
-/* Modify the random_seed string to VAL.  Return its previous
-   value.  */
+/* Set flag_random_seed to VAL, and if non-null, reinitialize random_seed.  */
 
-const char *
+void
 set_random_seed (const char *val)
 {
-  const char *old = flag_random_seed;
   flag_random_seed = val;
   if (flag_random_seed)
     {
@@ -318,7 +304,6 @@  set_random_seed (const char *val)
       if (!(endp > flag_random_seed && *endp == 0))
         random_seed = crc32_string (0, flag_random_seed);
     }
-  return old;
 }
 
 /* Handler for fatal signals, such as SIGSEGV.  These are transformed
@@ -818,11 +803,6 @@  print_switch_values (print_switch_fn_type print_fn)
   int pos = 0;
   size_t j;
 
-  /* Fill in the -frandom-seed option, if the user didn't pass it, so
-     that it can be printed below.  This helps reproducibility.  */
-  if (!flag_random_seed)
-    init_random_seed ();
-
   /* Print the options as passed.  */
   pos = print_single_switch (print_fn, pos,
 			     SWITCH_TYPE_DESCRIPTIVE, _("options passed: "));
diff --git a/gcc/toplev.h b/gcc/toplev.h
index 2f6b587d233..aed806eb28f 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -91,7 +91,7 @@  extern bool set_src_pwd		       (const char *);
 /* Functions used to manipulate the random seed.  */
 
 extern HOST_WIDE_INT get_random_seed (bool);
-extern const char *set_random_seed (const char *);
+extern void set_random_seed (const char *);
 
 extern void initialize_rtl (void);