Date: Mon, 6 Oct 2003 12:20:14 -0700 (PDT) From: Mark Valentine <mark@valentine.me.uk> To: freebsd-bugs@FreeBSD.org Subject: Re: bin/57554: [PATCH] sh(1) incorrect handling of quoted parameter expansion Message-ID: <200310061920.h96JKEAD076068@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/57554; it has been noted by GNATS. From: Mark Valentine <mark@valentine.me.uk> To: FreeBSD-gnats-submit@freebsd.org Cc: Subject: Re: bin/57554: [PATCH] sh(1) incorrect handling of quoted parameter expansion Date: Mon, 6 Oct 2003 20:15:38 +0000 The NetBSD revisions I mentioned do seem to fix this. Here's a patch which works for FreeBSD 4.8-STABLE; I just started a buildworld on my system (which is running an older 4.8-STABLE from June), and I'll do another installworld/buildworld cycle after that and follow up if any problems arise. The patch also applies OK to -CURRENT, but it'll take me a bit longer to get around to testing that. Index: mksyntax.c =================================================================== RCS file: /usr/cvs/src/bin/sh/mksyntax.c,v retrieving revision 1.14.2.3 diff -u -r1.14.2.3 mksyntax.c --- mksyntax.c 19 Jul 2002 04:38:51 -0000 1.14.2.3 +++ mksyntax.c 6 Oct 2003 18:21:50 -0000 @@ -70,7 +70,6 @@ { "CBACK", "a backslash character" }, { "CSQUOTE", "single quote" }, { "CDQUOTE", "double quote" }, - { "CENDQUOTE", "a terminating quote" }, { "CBQUOTE", "backwards single quote" }, { "CVAR", "a dollar sign" }, { "CENDVAR", "a '}' character" }, @@ -220,7 +219,7 @@ fputs("\n/* syntax table used when in double quotes */\n", cfile); add("\n", "CNL"); add("\\", "CBACK"); - add("\"", "CENDQUOTE"); + add("\"", "CDQUOTE"); add("`", "CBQUOTE"); add("$", "CVAR"); add("}", "CENDVAR"); @@ -230,7 +229,7 @@ init(); fputs("\n/* syntax table used when in single quotes */\n", cfile); add("\n", "CNL"); - add("'", "CENDQUOTE"); + add("'", "CSQUOTE"); /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ add("!*?[=~:/-", "CCTL"); print("sqsyntax"); Index: parser.c =================================================================== RCS file: /usr/cvs/src/bin/sh/parser.c,v retrieving revision 1.29.2.10 diff -u -r1.29.2.10 parser.c --- parser.c 22 Jul 2003 13:11:26 -0000 1.29.2.10 +++ parser.c 6 Oct 2003 18:39:24 -0000 @@ -73,6 +73,8 @@ /* values returned by readtoken */ #include "token.h" +#define OPENBRACE '{' +#define CLOSEBRACE '}' struct heredoc { @@ -885,6 +887,28 @@ #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} +/* + * Keep track of nested doublequotes in dblquote and doublequotep. + * We use dblquote for the first 32 levels, and we expand to a malloc'ed + * region for levels above that. Usually we never need to malloc. + * This code assumes that an int is 32 bits. We don't use uint32_t, + * because the rest of the code does not. + */ +#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \ + (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32)))) + +#define SETDBLQUOTE() \ + if (varnest < 32) \ + dblquote |= (1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32)) + +#define CLRDBLQUOTE() \ + if (varnest < 32) \ + dblquote &= ~(1 << varnest); \ + else \ + dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32)) + STATIC int readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) { @@ -894,6 +918,8 @@ char line[EOFMARKLEN + 1]; struct nodelist *bqlist; int quotef; + int *dblquotep = NULL; + size_t maxnest = 32; int dblquote; int varnest; /* levels of variables expansion */ int arinest; /* levels of arithmetic expansion */ @@ -903,6 +929,8 @@ int synentry; #if __GNUC__ /* Avoid longjmp clobbering */ + (void) &maxnest; + (void) &dblquotep; (void) &out; (void) "ef; (void) &dblquote; @@ -917,11 +945,12 @@ startlinno = plinno; dblquote = 0; - if (syntax == DQSYNTAX) - dblquote = 1; + varnest = 0; + if (syntax == DQSYNTAX) { + SETDBLQUOTE(); + } quotef = 0; bqlist = NULL; - varnest = 0; arinest = 0; parenlevel = 0; @@ -959,7 +988,7 @@ USTPUTC(c, out); break; case CCTL: - if (eofmark == NULL || dblquote) + if (eofmark == NULL || ISDBLQUOTE()) USTPUTC(CTLESC, out); USTPUTC(c, out); break; @@ -974,7 +1003,7 @@ else setprompt(0); } else { - if (dblquote && c != '\\' && + if (ISDBLQUOTE() && c != '\\' && c != '`' && c != '$' && (c != '"' || eofmark != NULL)) USTPUTC('\\', out); @@ -987,27 +1016,36 @@ } break; case CSQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = SQSYNTAX; - break; + if (syntax != SQSYNTAX) { + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = SQSYNTAX; + break; + } + /* FALLTHROUGH */ case CDQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = DQSYNTAX; - dblquote = 1; - break; - case CENDQUOTE: if (eofmark != NULL && arinest == 0 && varnest == 0) { USTPUTC(c, out); } else { if (arinest) { - syntax = ARISYNTAX; - dblquote = 0; + if (c != '"' || ISDBLQUOTE()) { + syntax = ARISYNTAX; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); + } } else if (eofmark == NULL) { - syntax = BASESYNTAX; - dblquote = 0; + if (c != '"' || ISDBLQUOTE()) { + syntax = BASESYNTAX; + CLRDBLQUOTE(); + } else { + syntax = DQSYNTAX; + SETDBLQUOTE(); + USTPUTC(CTLQUOTEMARK, out); + } } quotef++; } @@ -1015,8 +1053,8 @@ case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; - case CENDVAR: /* '}' */ - if (varnest > 0) { + case CENDVAR: /* CLOSEBRACE */ + if (varnest > 0 && !ISDBLQUOTE()) { varnest--; USTPUTC(CTLENDVAR, out); } else { @@ -1037,9 +1075,9 @@ USTPUTC(CTLENDARI, out); syntax = prevsyntax; if (syntax == DQSYNTAX) - dblquote = 1; + SETDBLQUOTE(); else - dblquote = 0; + CLRDBLQUOTE(); } else USTPUTC(')', out); } else { @@ -1092,6 +1130,8 @@ backquotelist = bqlist; grabstackblock(len); wordtext = out; + if (dblquotep != NULL) + ckfree(dblquotep); return lasttoken = TWORD; /* end of readtoken routine */ @@ -1202,7 +1242,7 @@ int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ c = pgetc(); - if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { + if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ @@ -1217,11 +1257,11 @@ typeloc = out - stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; - if (c == '{') { + if (c == OPENBRACE) { bracketed_name = 1; c = pgetc(); if (c == '#') { - if ((c = pgetc()) == '}') + if ((c = pgetc()) == CLOSEBRACE) c = '#'; else subtype = VSLENGTH; @@ -1281,11 +1321,17 @@ } else { pungetc(); } - if (subtype != VSLENGTH && (dblquote || arinest)) + if (subtype != VSLENGTH && (ISDBLQUOTE() || arinest)) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) + if (subtype != VSNORMAL) { varnest++; + if (varnest >= maxnest) { + dblquotep = ckrealloc(dblquotep, maxnest / 8); + dblquotep[(maxnest / 32) - 1] = 0; + maxnest += 32; + } + } } goto parsesub_return; } @@ -1366,7 +1412,7 @@ continue; } if (c != '\\' && c != '`' && c != '$' - && (!dblquote || c != '"')) + && (!ISDBLQUOTE() || c != '"')) STPUTC('\\', out); break; @@ -1437,7 +1483,7 @@ } parsebackquote = savepbq; handler = savehandler; - if (arinest || dblquote) + if (arinest || ISDBLQUOTE()) USTPUTC(CTLBACKQ | CTLQUOTE, out); else USTPUTC(CTLBACKQ, out); @@ -1456,7 +1502,7 @@ prevsyntax = syntax; syntax = ARISYNTAX; USTPUTC(CTLARI, out); - if (dblquote) + if (ISDBLQUOTE()) USTPUTC('"',out); else USTPUTC(' ',out); -- "Tigers will do ANYTHING for a tuna fish sandwich." "We're kind of stupid that way." *munch* *munch* -- <http://www.calvinandhobbes.com>
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200310061920.h96JKEAD076068>