Re: Performance Issue With OpenSSL 1.1.1c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jay Foster wrote in <84571f12-68b3-f7ee-7896-c891a2e253e7@xxxxxxxxxxxxxx>:
 |On 5/28/2019 10:39 AM, Jay Foster wrote:
 |> I built OpenSSL 1.1.1c from the recent release, but have noticed what 
 |> seems like a significant performance drop compared with 1.1.1b.  I 
 |> notice this when starting lighttpd.  With 1.1.1b, lighttpd starts in a 
 |> few seconds, but with 1.1.1c, it takes several minutes.
 |>
 |> I also noticed that with 1.1.1b, the CFLAGS automatically included 
 |> '-Wall -O3', but with 1.1.1c, '-Wall -O3' is no longer included in the 
 |> CFLAGS.  was this dropped?  I  added '-Wall -O3' to the CFLAGS, but 
 |> this did not seem to have any affect on the performance issue 
 |> (unrelated?).
 |>
 |> This is for a 32-bit ARM build.
 |>
 |> Jay
 |>
 |I think I have tracked down the change in 1.1.1c that is causing this.  
 |It is the addition of the DEVRANDOM_WAIT functionality for linux in 
 |e_os.h and crypto/rand/rand_unix.c.  lighttpd (libcrypto) is waiting in 
 |a select() call on /dev/random.  After this eventually wakes up, it then 
 |reads from /dev/urandom.  OpenSSL 1.1.1b did not do this, but instead 
 |just read from /dev/urandom.  Is there more information about this 
 |change (i.e., a rationale)?  I did not see anything in the CHANGES file 
 |about it.

I do not know why lighttpd ends up on /dev/random for you, but in
my opinion the Linux random stuff is both sophisticated and sucks.
The latter because (it seems that many) people end up using
haveged or similar to pimp up their entropy artificially, whereas
on the other side the initial OS seeding is no longer truly
supported.  Writing some seed to /dev/urandom does not bring any
entropy to the "real" pool.

This drove me insane on my elder boxes, and on my VM server (which
suddenly required minutes for booting, but mind you that was
actually really OpenSSH hanging on, just the boot messages made me
think something else) i even had to log in twice to end a hang of
half on hour -- by doing one (maybe two) keypress(es)!

Whereas that box does reasonable work by generating I/O and thus
I/O based entropy, once it is up.  But the pool cannot be feeded
until we get there.  I installed haveged, but this is ridiculous!
Therefore i have written a small program entropy-saver.c which
saves and restores entropy to the real pool, which is still
possible (though the interface is deprecated).
This works just fantastic, and even on my brand new laptop it is
of value.  And Linux does not take the proposed bits for granted
but about halfs that.  Feel free to use it.  Do not use it in
conjunction with haveged or something, or take care for the order.

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)
/*@ Save and load Linux (2.6.0+) entropy.
 *@ Different to "cat XY > /dev/urandom" this increments "entropy_avail".
 *@ NOTE: this will not work correctly if used in conjunction with haveged
 *@ or a similar entropy managing daemon; *unless* it is ensured it loads
 *@ entropy before, and saves entropy after the daemon lifetime!
 *@ Synopsis: entropy-saver save [file]
 *@ Synopsis: entropy-saver load [file]
 *@ "file" defaults to a_RAND_FILE_STORE.
 *
 * 2019 Steffen (Daode) Nurpmeso <steffen@xxxxxxxxxx>.
 * Public Domain
 */

/* Random device*/
#define a_RAND_DEV "/dev/random"

/* Maximum number of bytes we handle (must be LT INT_MAX/8!) */
#define a_RAND_NO_BYTES 512

/* When saving, the minimum number of entropy_avail we keep in the pool.
 * _This_ is checked after we have read once (512 we test initially).
 * We will refuse to save a dump which offers less than 128 bits. */
#define a_RAND_ENTROPY_COUNT_MIN 1024

/* Default storage */
#define a_RAND_FILE_STORE "/var/lib/misc/random.dat"

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <unistd.h>

#include <linux/random.h>
#include <linux/version.h>

#if KERNEL_VERSION(2,6,0) >= LINUX_VERSION_CODE
# error Linux kernel version and IOCTL usage incompatible.
#endif

static char const a_rand_file_store[] = a_RAND_FILE_STORE;

int
main(int argc, char **argv){
   enum {a_LOAD, a_SAVE};

   struct{
      struct rand_pool_info rpi;
      char buf[a_RAND_NO_BYTES];
   } x;
   char *rpibuf; /* To make C++ happy: rpi.buf is char[0] */
   ssize_t len;
   char const *store;
   int rv, accu, randfd, storfd, iocarg;

   /* Command line handling */
   if(argc < 2 || argc > 3)
      goto jeuse;

   if(!strcmp(argv[1], "load"))
      accu = a_LOAD;
   else if(!strcmp(argv[1], "save"))
      accu = a_SAVE;
   else{
jeuse:
      fprintf(stderr,
         "Synopsis: entropy-saver save [storage-file]\n"
         "Synopsis: entropy-saver load [storage-file]\n"
         "\n"
         "storage-file defaults to " a_RAND_FILE_STORE "\n"
         "Exit: sysexits.h: EX_USAGE, EX_NOPERM, EX_IOERR, EX_TEMPFAIL\n");
      rv = EX_USAGE;
      goto j_leave;
   }

   openlog("entropy-saver",
#ifdef LOG_PERROR
      LOG_PERROR |
#endif
      LOG_PID, LOG_DAEMON);

   /* Open our two files according to chosen action */
   randfd = open(a_RAND_DEV, (O_RDONLY | O_NONBLOCK));
   if(randfd == -1){
      accu = errno;
      syslog(LOG_ERR, "Failed to open " a_RAND_DEV ": %s\n",
         strerror(accu));
      rv = (accu == EACCES || accu == EPERM) ? EX_NOPERM : EX_IOERR;
      goto jleave;
   }

   store = (argv[2] != NULL) ? argv[2] : a_rand_file_store;
   storfd = open(store, (accu == a_LOAD ? O_RDONLY
         : O_WRONLY | O_CREAT | O_TRUNC), (S_IRUSR | S_IWUSR));
   if(storfd == -1){
      accu = errno;
      syslog(LOG_ERR, "Failed to open %s: %s\n", store, strerror(accu));
      rv = (accu == EACCES || accu == EPERM) ? EX_NOPERM : EX_IOERR;
      goto jerr1;
   }

   /* For at least statistics query entropy count once */
   rv = ioctl(randfd, (int)RNDGETENTCNT, &iocarg);
   if(rv == -1){
      syslog(LOG_ERR, "Failed to query available entropy of " a_RAND_DEV
         ": %s\n", strerror(errno));
      rv = EX_IOERR;
      goto jerr2;
   }
   x.rpi.entropy_count = iocarg;

   rpibuf = (char*)x.rpi.buf;

   if(accu == a_LOAD){
      syslog(LOG_INFO, "%d bits of entropy available at " a_RAND_DEV "\n",
         x.rpi.entropy_count);

      /* INT: entropy bits */
      len = read(storfd, &x.rpi.entropy_count, sizeof x.rpi.entropy_count);
      if(len == -1){
         syslog(LOG_ERR, "Failed to read from %s: %s\n",
            store, strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }
      if(len != sizeof x.rpi.entropy_count){
         syslog(LOG_ERR, "Storage %s: seems corrupted (false format)\n",
            store);
         rv = EX_IOERR;
         goto jerr2;
      }
      /* The former because we will refuse to save less than that, the latter
       * rather arbitrary (like the suff as such?) */
      if(x.rpi.entropy_count < 128 || x.rpi.entropy_count > 1000000){
         syslog(LOG_ERR, "Storage %s seems corrupted (%d entropy bits)\n",
            store, x.rpi.entropy_count);
         rv = EX_IOERR;
         goto jerr2;
      }

      /* REST: pool bytes */
      len = read(storfd, rpibuf, sizeof x.buf);
      if(len == -1){
         syslog(LOG_ERR, "Failed to read from %s: %s\n",
            store, strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }
      if(len == 0){
         syslog(LOG_ERR, "Storage %s: seems corrupted (no entropy)\n", store);
         rv = EX_TEMPFAIL;
         goto jerr2;
      }
      x.rpi.buf_size = (int)len;

      if(read(storfd, &accu, 1) != 0){
         syslog(LOG_ERR, "Storage %s: seems corrupted (no EOF seen)\n",
            store);
         rv = EX_IOERR;
         goto jerr2;
      }

      syslog(LOG_INFO, "%d bytes / %d bits of entropy read from %s\n",
         x.rpi.buf_size, x.rpi.entropy_count, store);

      accu = ioctl(randfd, RNDADDENTROPY, &x.rpi);
      if(accu == -1){
         syslog(LOG_ERR, "Failed to add %d bits of entropy to " a_RAND_DEV
            ": %s\n", x.rpi.entropy_count, strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }

      /* For at least statistics */
      rv = ioctl(randfd, (int)RNDGETENTCNT, &iocarg);
      if(rv != -1)
         syslog(LOG_INFO, "%d bits of entropy are at at " a_RAND_DEV "\n",
            iocarg);
   }else{
      /* Since we are reading in non-blocking mode, and since reading from
       * /dev/random returns not that much in this mode, read in a loop until
       * it no longer serves / the entropy count falls under a a_RAND_ */
      size_t rem_size;
      int entrop_cnt, e;

      entrop_cnt = x.rpi.entropy_count;
      syslog(LOG_INFO, "%d bits of entropy available at " a_RAND_DEV "%s\n",
         entrop_cnt, (entrop_cnt <= 512 ? ": temporary failure" : ""));
      if(entrop_cnt <= 512){
         rv = EX_TEMPFAIL;
         goto jerr2;
      }

      x.rpi.buf_size = x.rpi.entropy_count = 0;
      rem_size = sizeof x.buf;
jread_more:
      len = read(randfd, rpibuf, rem_size);
      if(len == -1){
         /* Ignore the EAGAIN that /dev/random reports when it would block
          * (Bernd Petrovitsch (bernd at petrovitsch dot priv dot at)) */
         if((e = errno) == EAGAIN ||
#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN /* xxx never true on Linux */
               e == EWOULDBLOCK ||
#endif
               e == EBUSY)
            goto jread_done;

         syslog(LOG_ERR, "Failed to read from " a_RAND_DEV ": %s\n",
            strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }
      x.rpi.buf_size += (int)len;
      rpibuf += len;
      rem_size -= len;

      rv = ioctl(randfd, (int)RNDGETENTCNT, &iocarg);
      if(rv == -1){
         syslog(LOG_ERR, "Failed to query remaining entropy of " a_RAND_DEV
            ": %s\n", strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }
      entrop_cnt -= iocarg;
      x.rpi.entropy_count += entrop_cnt;

      /* Try to read more? */
      if(len > 0 && (entrop_cnt = iocarg) >= a_RAND_ENTROPY_COUNT_MIN &&
            rem_size >= 64)
         goto jread_more;
      syslog(LOG_INFO, "%d bits of entropy remain at " a_RAND_DEV "\n",
         iocarg);

jread_done:
      if(x.rpi.entropy_count <= 128){
         syslog(LOG_ERR, "Insufficient entropy to save from " a_RAND_DEV
            " (%d bits)\n", x.rpi.entropy_count);
         rv = EX_TEMPFAIL;
         goto jerr2;
      }

      rpibuf = (char*)x.rpi.buf;
      len = x.rpi.buf_size;
      if((ssize_t)sizeof(x.rpi.entropy_count) != write(storfd,
               &x.rpi.entropy_count, sizeof x.rpi.entropy_count) ||
            len != write(storfd, rpibuf, len)){
         syslog(LOG_ERR, "Failed to write to %s: %s\n",
            store, strerror(errno));
         rv = EX_IOERR;
         goto jerr2;
      }

      syslog(LOG_INFO, "%d bits / %d bytes of entropy saved to %s\n",
         x.rpi.entropy_count, x.rpi.buf_size, store);
   }

   rv = 0;
jerr2:
   if(close(storfd) == -1)
      syslog(LOG_ERR, "Error closing %s: %s\n", store, strerror(errno));
jerr1:
   if(close(randfd) == -1)
      syslog(LOG_ERR, "Error closing " a_RAND_DEV ": %s\n", strerror(errno));
jleave:
   closelog();
j_leave:
   return rv;
}

/* s-it-mode */

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux