]> Savannah Git Hosting - gnulib.git/commitdiff
af_alg: recover better from crypto failures
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 10 May 2018 01:07:29 +0000 (18:07 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 10 May 2018 01:08:58 +0000 (18:08 -0700)
* lib/af_alg.c (afalg_stream): Recover from crypto failures if the
input stream is seekable, by repositioning the stream back to
where it was, possibly by just calling sendfile with an offset
arg.  This lets us return -EAFNOSUPPORT instead of -EIO in some
cases, which lets our callers try again with user-mode code.
* modules/crypto/af_alg (Depends-on): Depend on fseeko and ftello
instead of on fflush and lseek.

ChangeLog
lib/af_alg.c
lib/af_alg.h
modules/crypto/af_alg

index 3a9dc63ec4f489ab1cc3126f8f2485d8f3f420a2..4eb0958583b9046a3d38321d5a20fb558ff04051 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2018-05-09  Paul Eggert  <eggert@cs.ucla.edu>
 
+       af_alg: recover better from crypto failures
+       * lib/af_alg.c (afalg_stream): Recover from crypto failures if the
+       input stream is seekable, by repositioning the stream back to
+       where it was, possibly by just calling sendfile with an offset
+       arg.  This lets us return -EAFNOSUPPORT instead of -EIO in some
+       cases, which lets our callers try again with user-mode code.
+       * modules/crypto/af_alg (Depends-on): Depend on fseeko and ftello
+       instead of on fflush and lseek.
+
        af_alg: distiguish I/O errors better
        * lib/af_alg.c (afalg_buffer, afalg_stream): Return -EAFNOSUPPORT,
        not -EIO, if it’s OK for the caller to try again with user-mode code.
index cfc31529afbc70404f9db55eff9495150b1e2f54..677a8e34044e5ec770f3c9e4e4c0e75bafbaa346 100644 (file)
@@ -103,33 +103,27 @@ afalg_stream (FILE *stream, const char *alg,
   if (ofd < 0)
     return ofd;
 
-  /* if file is a regular file, attempt sendfile to pipe the data.  */
+  /* If STREAM's size is known and nonzero and not too large, attempt
+     sendfile to pipe the data.  The nonzero restriction avoids issues
+     with /proc files that pretend to be empty, and lets the classic
+     read-write loop work around an empty-input bug noted below.  */
   int fd = fileno (stream);
   int result = 0;
   struct stat st;
-  if (fstat (fd, &st) == 0
+  off_t nseek = 0, off = ftello (stream);
+  if (0 <= off && fstat (fd, &st) == 0
       && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st) || S_TYPEISTMO (&st))
-      && 0 < st.st_size && st.st_size <= SYS_BUFSIZE_MAX)
+      && off < st.st_size && st.st_size - off < SYS_BUFSIZE_MAX)
     {
-      /* Make sure the offset of fileno (stream) reflects how many bytes
-         have been read from stream before this function got invoked.
-         Note: fflush on an input stream after ungetc does not work as expected
-         on some platforms.  Therefore this situation is not supported here.  */
-      if (fflush (stream))
-        result = -EIO;
-      else
-        {
-          off_t nbytes = st.st_size - lseek (fd, 0, SEEK_CUR);
-          /* On Linux < 4.9, the value for an empty stream is wrong (all 0).
-             See <https://patchwork.kernel.org/patch/9308641/>.  */
-          if (nbytes <= 0 || sendfile (ofd, fd, NULL, nbytes) != nbytes)
-            result = -EAFNOSUPPORT;
-        }
+      off_t nbytes = st.st_size - off;
+      if (sendfile (ofd, fd, &off, nbytes) != nbytes)
+        result = -EAFNOSUPPORT;
     }
   else
     {
-      /* sendfile not possible, do a classic read-write loop.  */
-      int non_empty = 0;
+     /* sendfile not possible, do a classic read-write loop.
+        Defer to glibc as to whether EOF is sticky; see
+        <https://sourceware.org/bugzilla/show_bug.cgi?id=19476>.  */
       for (;;)
         {
           char buf[BLOCKSIZE];
@@ -138,25 +132,28 @@ afalg_stream (FILE *stream, const char *alg,
             {
               /* On Linux < 4.9, the value for an empty stream is wrong (all 0).
                  See <https://patchwork.kernel.org/patch/9308641/>.  */
-              if (!non_empty)
+              if (nseek == 0)
                 result = -EAFNOSUPPORT;
 
               if (ferror (stream))
                 result = -EIO;
               break;
             }
-          non_empty = 1;
+          nseek -= size;
           if (send (ofd, buf, size, MSG_MORE) != size)
             {
-              result = -EIO;
+              result = -EAFNOSUPPORT;
               break;
             }
         }
     }
 
   if (result == 0 && read (ofd, resblock, hashlen) != hashlen)
-    result = -EIO;
+    result = -EAFNOSUPPORT;
   close (ofd);
+  if (result == -EAFNOSUPPORT && nseek != 0
+      && fseeko (stream, nseek, SEEK_CUR) != 0)
+    result = -EIO;
   return result;
 }
 
index 018fa221a0da109e2b8291d58368a914c3baefcc..429ec210d6d9694c78a0865d7d2e9a13beb82467 100644 (file)
@@ -1,4 +1,4 @@
-/* af_alg.h - Compute message digests from file streams.
+/* af_alg.h - Compute message digests from file streams and buffers.
    Copyright (C) 2018 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
@@ -61,11 +61,12 @@ int
 afalg_buffer (const char *buffer, size_t len, const char *alg,
               void *resblock, ssize_t hashlen);
 
-/* Compute a message digest of the contents of a file.
+/* Compute a message digest of data read from STREAM.
 
-   STREAM is an open file stream.  Regular files are handled more efficiently.
-   The contents of STREAM from its current position to its end will be read.
-   The case that the last operation on STREAM was an 'ungetc' is not supported.
+   STREAM is an open file stream.  The last operation on STREAM should
+   not be 'ungetc', and if STREAM is also open for writing it should
+   have been fflushed since its last write.  Read from the current
+   position to the end of STREAM.  Handle regular files efficently.
 
    ALG is the message digest algorithm; see the file /proc/crypto.
 
@@ -82,7 +83,9 @@ afalg_buffer (const char *buffer, size_t len, const char *alg,
       sha512 | 64
 
    If successful, fill RESBLOCK and return 0.
-   Upon failure, return a negated error number.  */
+   Upon failure, return a negated error number.
+   Unless returning 0 or -EIO, restore STREAM's file position so that
+   the caller can fall back on some other method.  */
 int
 afalg_stream (FILE *stream, const char *alg,
               void *resblock, ssize_t hashlen);
index 0c498e05c8eb096278b87b7ca68cfda0c6a31420..519bab7bfcd738ce596d2a170e278fa1814411a0 100644 (file)
@@ -8,8 +8,8 @@ lib/sys-limits.h
 m4/af_alg.m4
 
 Depends-on:
-fflush          [test $USE_AF_ALG = 1]
-lseek           [test $USE_AF_ALG = 1]
+fseeko          [test $USE_AF_ALG = 1]
+ftello          [test $USE_AF_ALG = 1]
 sys_socket
 sys_stat