ash: input: Allow two consecutive calls to pungetc

Upstream commit:

    input: Allow two consecutive calls to pungetc

    The commit ef91d3d6a4c39421fd3a391e02cd82f9f3aee4a8 ([PARSER]
    Handle backslash newlines properly after dollar sign) created
    cases where we make two consecutive calls to pungetc.  As we
    don't explicitly support that there are corner cases where you
    end up with garbage input leading to undefined behaviour.

    This patch adds explicit support for two consecutive calls to
    pungetc.

    Reported-by: Jilles Tjoelker <jilles@stack.nl>
    Reported-by: Juergen Daubert <jue@jue.li>
    Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

In bbox case, bashism >& may need two pungetc() too.

function                                             old     new   delta
pgetc                                                514     555     +41
pushstring                                           114     144     +30
basepf                                                52      76     +24
popstring                                            134     151     +17
parse_command                                       1584    1585      +1
pungetc                                               12       9      -3
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/1 up/down: 113/-3)            Total: 110 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2016-09-29 02:11:19 +02:00
parent 78c9c736ab
commit 3b4d04b77e

View File

@ -1168,6 +1168,12 @@ struct strpush {
struct alias *ap; /* if push was associated with an alias */ struct alias *ap; /* if push was associated with an alias */
#endif #endif
char *string; /* remember the string since it may change */ char *string; /* remember the string since it may change */
/* Remember last two characters for pungetc. */
int lastc[2];
/* Number of outstanding calls to pungetc. */
int unget;
}; };
struct parsefile { struct parsefile {
@ -1180,6 +1186,12 @@ struct parsefile {
char *buf; /* input buffer */ char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */ struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */ struct strpush basestrpush; /* so pushing one is fast */
/* Remember last two characters for pungetc. */
int lastc[2];
/* Number of outstanding calls to pungetc. */
int unget;
}; };
static struct parsefile basepf; /* top level input file */ static struct parsefile basepf; /* top level input file */
@ -9715,6 +9727,8 @@ pushstring(char *s, struct alias *ap)
g_parsefile->strpush = sp; g_parsefile->strpush = sp;
sp->prev_string = g_parsefile->next_to_pgetc; sp->prev_string = g_parsefile->next_to_pgetc;
sp->prev_left_in_line = g_parsefile->left_in_line; sp->prev_left_in_line = g_parsefile->left_in_line;
sp->unget = g_parsefile->unget;
memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
#if ENABLE_ASH_ALIAS #if ENABLE_ASH_ALIAS
sp->ap = ap; sp->ap = ap;
if (ap) { if (ap) {
@ -9724,6 +9738,7 @@ pushstring(char *s, struct alias *ap)
#endif #endif
g_parsefile->next_to_pgetc = s; g_parsefile->next_to_pgetc = s;
g_parsefile->left_in_line = len; g_parsefile->left_in_line = len;
g_parsefile->unget = 0;
INT_ON; INT_ON;
} }
@ -9751,6 +9766,8 @@ popstring(void)
#endif #endif
g_parsefile->next_to_pgetc = sp->prev_string; g_parsefile->next_to_pgetc = sp->prev_string;
g_parsefile->left_in_line = sp->prev_left_in_line; g_parsefile->left_in_line = sp->prev_left_in_line;
g_parsefile->unget = sp->unget;
memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
g_parsefile->strpush = sp->prev; g_parsefile->strpush = sp->prev;
if (sp != &(g_parsefile->basestrpush)) if (sp != &(g_parsefile->basestrpush))
free(sp); free(sp);
@ -9846,13 +9863,14 @@ preadfd(void)
*/ */
//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
#define pgetc_debug(...) ((void)0) #define pgetc_debug(...) ((void)0)
static int pgetc(void);
static int static int
preadbuffer(void) preadbuffer(void)
{ {
char *q; char *q;
int more; int more;
while (g_parsefile->strpush) { if (g_parsefile->strpush) {
#if ENABLE_ASH_ALIAS #if ENABLE_ASH_ALIAS
if (g_parsefile->left_in_line == -1 if (g_parsefile->left_in_line == -1
&& g_parsefile->strpush->ap && g_parsefile->strpush->ap
@ -9864,13 +9882,7 @@ preadbuffer(void)
} }
#endif #endif
popstring(); popstring();
/* try "pgetc" now: */ return pgetc();
pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
g_parsefile->left_in_line,
g_parsefile->next_to_pgetc,
g_parsefile->next_to_pgetc);
if (--g_parsefile->left_in_line >= 0)
return (unsigned char)(*g_parsefile->next_to_pgetc++);
} }
/* on both branches above g_parsefile->left_in_line < 0. /* on both branches above g_parsefile->left_in_line < 0.
* "pgetc" needs refilling. * "pgetc" needs refilling.
@ -9949,27 +9961,28 @@ preadbuffer(void)
return (unsigned char)*g_parsefile->next_to_pgetc++; return (unsigned char)*g_parsefile->next_to_pgetc++;
} }
#define pgetc_as_macro() \
(--g_parsefile->left_in_line >= 0 \
? (unsigned char)*g_parsefile->next_to_pgetc++ \
: preadbuffer() \
)
static int static int
pgetc(void) pgetc(void)
{ {
pgetc_debug("pgetc_fast at %d:%p'%s'", int c;
pgetc_debug("pgetc at %d:%p'%s'",
g_parsefile->left_in_line, g_parsefile->left_in_line,
g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc,
g_parsefile->next_to_pgetc); g_parsefile->next_to_pgetc);
return pgetc_as_macro(); if (g_parsefile->unget)
} return g_parsefile->lastc[--g_parsefile->unget];
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE if (--g_parsefile->left_in_line >= 0)
# define pgetc_fast() pgetc() c = (signed char)*g_parsefile->next_to_pgetc++;
#else else
# define pgetc_fast() pgetc_as_macro() c = preadbuffer();
#endif
g_parsefile->lastc[1] = g_parsefile->lastc[0];
g_parsefile->lastc[0] = c;
return c;
}
#if ENABLE_ASH_ALIAS #if ENABLE_ASH_ALIAS
static int static int
@ -9977,11 +9990,11 @@ pgetc_without_PEOA(void)
{ {
int c; int c;
do { do {
pgetc_debug("pgetc_fast at %d:%p'%s'", pgetc_debug("pgetc at %d:%p'%s'",
g_parsefile->left_in_line, g_parsefile->left_in_line,
g_parsefile->next_to_pgetc, g_parsefile->next_to_pgetc,
g_parsefile->next_to_pgetc); g_parsefile->next_to_pgetc);
c = pgetc_fast(); c = pgetc();
} while (c == PEOA); } while (c == PEOA);
return c; return c;
} }
@ -10015,18 +10028,13 @@ pfgets(char *line, int len)
} }
/* /*
* Undo the last call to pgetc. Only one character may be pushed back. * Undo a call to pgetc. Only two characters may be pushed back.
* PEOF may be pushed back. * PEOF may be pushed back.
*/ */
static void static void
pungetc(void) pungetc(void)
{ {
g_parsefile->left_in_line++; g_parsefile->unget++;
g_parsefile->next_to_pgetc--;
pgetc_debug("pushed back to %d:%p'%s'",
g_parsefile->left_in_line,
g_parsefile->next_to_pgetc,
g_parsefile->next_to_pgetc);
} }
/* /*
@ -10043,6 +10051,7 @@ pushfile(void)
pf->pf_fd = -1; pf->pf_fd = -1;
/*pf->strpush = NULL; - ckzalloc did it */ /*pf->strpush = NULL; - ckzalloc did it */
/*pf->basestrpush.prev = NULL;*/ /*pf->basestrpush.prev = NULL;*/
/*pf->unget = 0;*/
g_parsefile = pf; g_parsefile = pf;
} }
@ -11451,7 +11460,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
IF_ASH_ALIAS(if (c != PEOA)) IF_ASH_ALIAS(if (c != PEOA))
USTPUTC(c, out); USTPUTC(c, out);
} }
c = pgetc_fast(); c = pgetc();
} /* for (;;) */ } /* for (;;) */
endword: endword:
@ -11945,7 +11954,7 @@ xxreadtoken(void)
setprompt_if(needprompt, 2); setprompt_if(needprompt, 2);
startlinno = g_parsefile->linno; startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */ for (;;) { /* until token or start of word found */
c = pgetc_fast(); c = pgetc();
if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
continue; continue;
@ -12008,7 +12017,7 @@ xxreadtoken(void)
setprompt_if(needprompt, 2); setprompt_if(needprompt, 2);
startlinno = g_parsefile->linno; startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */ for (;;) { /* until token or start of word found */
c = pgetc_fast(); c = pgetc();
switch (c) { switch (c) {
case ' ': case '\t': case ' ': case '\t':
IF_ASH_ALIAS(case PEOA:) IF_ASH_ALIAS(case PEOA:)