>From 308f08a31e7e78be24a7953ed27ab9d8341a357d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Wed, 18 Feb 2015 02:23:35 +0000 Subject: [PATCH] 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 --- ChangeLog | 10 ++++++++++ lib/getopt.c | 50 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5083e95..8cc2dbf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2015-02-18 Pádraig Brady + + 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-16 Paul Eggert getdtablesize, dup2, fcntl: port to Android diff --git a/lib/getopt.c b/lib/getopt.c index 3b9c585..7e89c37 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -487,7 +487,20 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, 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,29 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, || 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; + } + 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 +611,20 @@ _getopt_internal_r (int argc, char **argv, const char *optstring, 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) { -- 2.1.0