nscd restart: Use malloc instead of extend_alloca [BZ #18023]
authorFlorian Weimer <fweimer@redhat.com>
Mon, 25 Jun 2018 15:10:15 +0000 (17:10 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Mon, 25 Jun 2018 15:10:15 +0000 (17:10 +0200)
This introduces a separate function, read_cmdline, which reads the
contents of /proc/self/cmdline into a heap-allocated buffer.

ChangeLog
nscd/connections.c

index b10df610e6ffdaa619257d928d65b9874a0f25b1..4da861699c557de53ad236eb81e4c29bd0ba176a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2018-06-25  Florian Weimer  <fweimer@redhat.com>
+
+       [BZ #18023]
+       * nscd/connections.c (read_cmdline): New function.
+       (restart): Use it.  Update comment.
+
 2018-06-25  Rafal Luzynski  <digitalfreak@lingonborough.com>
 
        [BZ #23140]
index 1b3bae4eebdafa1bbc44d267f9652ff722962e40..47fbb9923aa2aac7480f15a823303862d3049363 100644 (file)
@@ -1281,64 +1281,83 @@ request from '%s' [%ld] not handled due to missing permission"),
     }
 }
 
-
-/* Restart the process.  */
-static void
-restart (void)
+static char *
+read_cmdline (size_t *size)
 {
-  /* First determine the parameters.  We do not use the parameters
-     passed to main() since in case nscd is started by running the
-     dynamic linker this will not work.  Yes, this is not the usual
-     case but nscd is part of glibc and we occasionally do this.  */
-  size_t buflen = 1024;
-  char *buf = alloca (buflen);
-  size_t readlen = 0;
   int fd = open ("/proc/self/cmdline", O_RDONLY);
-  if (fd == -1)
+  if (fd < 0)
+    return NULL;
+  size_t current = 0;
+  size_t limit = 1024;
+  char *buffer = malloc (limit);
+  if (buffer == NULL)
     {
-      dbg_log (_("\
-cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
-              strerror (errno));
-
-      paranoia = 0;
-      return;
+      close (fd);
+      errno = ENOMEM;
+      return NULL;
     }
-
   while (1)
     {
-      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + readlen,
-                                           buflen - readlen));
-      if (n == -1)
+      if (current == limit)
        {
-         dbg_log (_("\
-cannot read /proc/self/cmdline: %s; disabling paranoia mode"),
-                  strerror (errno));
+         char *newptr;
+         if (2 * limit < limit
+             || (newptr = realloc (buffer, 2 * limit)) == NULL)
+           {
+             free (buffer);
+             close (fd);
+             errno = ENOMEM;
+             return NULL;
+           }
+         buffer = newptr;
+         limit *= 2;
+       }
 
+      ssize_t n = TEMP_FAILURE_RETRY (read (fd, buffer + current,
+                                           limit - current));
+      if (n == -1)
+       {
+         int e = errno;
+         free (buffer);
          close (fd);
-         paranoia = 0;
-         return;
+         errno = e;
+         return NULL;
        }
-
-      readlen += n;
-
-      if (readlen < buflen)
+      if (n == 0)
        break;
-
-      /* We might have to extend the buffer.  */
-      size_t old_buflen = buflen;
-      char *newp = extend_alloca (buf, buflen, 2 * buflen);
-      buf = memmove (newp, buf, old_buflen);
+      current += n;
     }
 
   close (fd);
+  *size = current;
+  return buffer;
+}
+
+
+/* Restart the process.  */
+static void
+restart (void)
+{
+  /* First determine the parameters.  We do not use the parameters
+     passed to main because then nscd would use the system libc after
+     restarting even if it was started by a non-system dynamic linker
+     during glibc testing.  */
+  size_t readlen;
+  char *cmdline = read_cmdline (&readlen);
+  if (cmdline == NULL)
+    {
+      dbg_log (_("\
+cannot open /proc/self/cmdline: %m; disabling paranoia mode"));
+      paranoia = 0;
+      return;
+    }
 
   /* Parse the command line.  Worst case scenario: every two
      characters form one parameter (one character plus NUL).  */
   char **argv = alloca ((readlen / 2 + 1) * sizeof (argv[0]));
   int argc = 0;
 
-  char *cp = buf;
-  while (cp < buf + readlen)
+  for (char *cp = cmdline; cp < cmdline + readlen;)
     {
       argv[argc++] = cp;
       cp = (char *) rawmemchr (cp, '\0') + 1;
@@ -1355,6 +1374,7 @@ cannot change to old UID: %s; disabling paranoia mode"),
                   strerror (errno));
 
          paranoia = 0;
+         free (cmdline);
          return;
        }
 
@@ -1366,6 +1386,7 @@ cannot change to old GID: %s; disabling paranoia mode"),
 
          ignore_value (setuid (server_uid));
          paranoia = 0;
+         free (cmdline);
          return;
        }
     }
@@ -1383,6 +1404,7 @@ cannot change to old working directory: %s; disabling paranoia mode"),
          ignore_value (setgid (server_gid));
        }
       paranoia = 0;
+      free (cmdline);
       return;
     }
 
@@ -1431,6 +1453,7 @@ cannot change to old working directory: %s; disabling paranoia mode"),
     dbg_log (_("cannot change current working directory to \"/\": %s"),
             strerror (errno));
   paranoia = 0;
+  free (cmdline);
 
   /* Reenable the databases.  */
   time_t now = time (NULL);