changeset 17920:7a288a43abf9

getopt: don't crash on memory exhaustion * lib/getopt.c (_getopt_internal_r): Use degraded diagnostics on memory exhaustion. In the _LIBC case we use alloca() as is already done in glibc, so we don't need to consider the separate error path in that awkward case. Also fix a memory leak when ambiguous options are present. Reported by Tobias Stoeckmann
author Pádraig Brady <P@draigBrady.com>
date Wed, 18 Feb 2015 02:23:35 +0000
parents c3b0e9dd56c5
children 58c1b50299a6
files ChangeLog lib/getopt.c
diffstat 2 files changed, 50 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2015-02-18  Pádraig Brady  <P@draigBrady.com>
+
+	getopt: don't crash on memory exhaustion
+	* lib/getopt.c (_getopt_internal_r): Use degraded diagnostics on
+	memory exhaustion.  In the _LIBC case we use alloca() as is
+	already done in glibc, so we don't need to consider the separate
+	error path in that awkward case.  Also fix a memory leak when
+	ambiguous options are present.
+	Reported by Tobias Stoeckmann
+
 2015-02-17  Mike Miller  <mtmiller@ieee.org>
 
 	tempname: allow compilation with C++ (trivial)
--- a/lib/getopt.c
+++ b/lib/getopt.c
@@ -487,7 +487,20 @@
         const struct option *p;
         struct option_list *next;
       } *ambig_list = NULL;
+#ifdef _LIBC
+/* malloc() not used for _LIBC to simplify failure messages.  */
+# define free_option_list(l)
+#else
+# define free_option_list(l)			\
+      while (l != NULL)				\
+        {					\
+          struct option_list *pn = l->next;	\
+          free (l);				\
+          l = pn;				\
+        }
+#endif
       int exact = 0;
+      int ambig = 0;
       int indfound = -1;
       int option_index;
 
@@ -520,16 +533,30 @@
                      || pfound->val != p->val)
               {
                 /* Second or later nonexact match found.  */
+#ifdef _LIBC
+                struct option_list *newp = alloca (sizeof (*newp));
+#else
                 struct option_list *newp = malloc (sizeof (*newp));
-                newp->p = p;
-                newp->next = ambig_list;
-                ambig_list = newp;
+                if (newp == NULL)
+                  {
+                    ambig = 1; /* Use simpler fallback message.  */
+                    free_option_list (ambig_list);
+                    ambig_list = NULL;
+                    break;
+                  }
+                else
+#endif
+                  {
+                    newp->p = p;
+                    newp->next = ambig_list;
+                    ambig_list = newp;
+                  }
               }
           }
 
-      if (ambig_list != NULL && !exact)
+      if ((ambig || ambig_list) && !exact)
         {
-          if (print_errors)
+          if (print_errors && ambig_list)
             {
               struct option_list first;
               first.p = pfound;
@@ -585,18 +612,20 @@
               fputc ('\n', stderr);
 #endif
             }
+          else if (print_errors && ambig)
+            {
+              fprintf (stderr,
+                       _("%s: option '%s' is ambiguous\n"),
+                       argv[0], argv[d->optind]);
+            }
           d->__nextchar += strlen (d->__nextchar);
           d->optind++;
           d->optopt = 0;
+          free_option_list (ambig_list);
           return '?';
         }
 
-      while (ambig_list != NULL)
-        {
-          struct option_list *pn = ambig_list->next;
-          free (ambig_list);
-          ambig_list = pn;
-        }
+      free_option_list (ambig_list);
 
       if (pfound != NULL)
         {