ash: exec: Stricter pathopt parsing
No binary (build failed)
—
(log yok)
| @@ -2557,8 +2557,31 @@ listvars(int on, int off, struct strlist *lp, char ***end) | |
| } | |
| - | /* ============ Path search helper |
| - | * |
| + | /* ============ Path search helper */ |
| + | static const char * |
| + | legal_pathopt(const char *opt, const char *term, int magic) |
| + | { |
| + | switch (magic) { |
| + | case 0: |
| + | opt = NULL; |
| + | break; |
| + | |
| + | case 1: |
| + | opt = prefix(opt, "builtin") ?: prefix(opt, "func"); |
| + | break; |
| + | |
| + | default: |
| + | opt += strcspn(opt, term); |
| + | break; |
| + | } |
| + | |
| + | if (opt && *opt == '%') |
| + | opt++; |
| + | |
| + | return opt; |
| + | } |
| + | |
| + | /* |
| * The variable path (passed by reference) should be set to the start | |
| * of the path before the first call; padvance will update | |
| * this value as it proceeds. Successive calls to padvance will return | |
| @@ -2566,40 +2589,70 @@ listvars(int on, int off, struct strlist *lp, char ***end) | |
| * a percent sign) appears in the path entry then the global variable | |
| * pathopt will be set to point to it; otherwise pathopt will be set to | |
| * NULL. | |
| + | * |
| + | * If magic is 0 then pathopt recognition will be disabled. If magic is |
| + | * 1 we shall recognise %builtin/%func. Otherwise we shall accept any |
| + | * pathopt. |
| */ | |
| static const char *pathopt; /* set by padvance */ | |
| static int | |
| - | padvance(const char **path, const char *name) |
| + | padvance_magic(const char **path, const char *name, int magic) |
| { | |
| + | const char *term = "%:"; |
| + | const char *lpathopt; |
| const char *p; | |
| char *q; | |
| const char *start; | |
| + | size_t qlen; |
| size_t len; | |
| if (*path == NULL) | |
| return -1; | |
| + | |
| + | lpathopt = NULL; |
| start = *path; | |
| - | for (p = start; *p && *p != ':' && *p != '%'; p++) |
| - | continue; |
| - | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ |
| - | q = growstackto(len); |
| - | if (p != start) { |
| - | q = mempcpy(q, start, p - start); |
| - | *q++ = '/'; |
| + | |
| + | if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { |
| + | lpathopt = start + 1; |
| + | start = p; |
| + | term = ":"; |
| } | |
| - | strcpy(q, name); |
| - | pathopt = NULL; |
| + | |
| + | len = strcspn(start, term); |
| + | p = start + len; |
| + | |
| if (*p == '%') { | |
| - | pathopt = ++p; |
| - | while (*p && *p != ':') |
| - | p++; |
| + | size_t extra = strchrnul(p, ':') - p; |
| + | |
| + | if (legal_pathopt(p + 1, term, magic)) |
| + | lpathopt = p + 1; |
| + | else |
| + | len += extra; |
| + | |
| + | p += extra; |
| } | |
| - | if (*p == ':') |
| - | *path = p + 1; |
| - | else |
| - | *path = NULL; |
| - | return len; |
| + | |
| + | pathopt = lpathopt; |
| + | *path = *p == ':' ? p + 1 : NULL; |
| + | |
| + | /* "2" is for '/' and '\0' */ |
| + | qlen = len + strlen(name) + 2; |
| + | q = growstackto(qlen); |
| + | |
| + | if (len) { |
| + | q = mempcpy(q, start, len); |
| + | *q++ = '/'; |
| + | } |
| + | strcpy(q, name); |
| + | |
| + | return qlen; |
| + | } |
| + | |
| + | static int |
| + | padvance(const char **path, const char *name) |
| + | { |
| + | return padvance_magic(path, name, 1); |
| } | |
| @@ -8217,11 +8270,10 @@ printentry(struct tblentry *cmdp) | |
| } | |
| /* | |
| - | * Clear out command entries. The argument specifies the first entry in |
| - | * PATH which has changed. |
| + | * Clear out command entries. |
| */ | |
| static void | |
| - | clearcmdentry(int firstchange) |
| + | clearcmdentry(void) |
| { | |
| struct tblentry **tblp; | |
| struct tblentry **pp; | |
| @@ -8231,10 +8283,8 @@ clearcmdentry(int firstchange) | |
| for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) { | |
| pp = tblp; | |
| while ((cmdp = *pp) != NULL) { | |
| - | if ((cmdp->cmdtype == CMDNORMAL && |
| - | cmdp->param.index >= firstchange) |
| - | || (cmdp->cmdtype == CMDBUILTIN && |
| - | builtinloc >= firstchange) |
| + | if (cmdp->cmdtype == CMDNORMAL |
| + | || (cmdp->cmdtype == CMDBUILTIN && builtinloc > 0) |
| ) { | |
| *pp = cmdp->next; | |
| free(cmdp); | |
| @@ -8334,7 +8384,7 @@ hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |
| char *name; | |
| if (nextopt("r") != '\0') { | |
| - | clearcmdentry(0); |
| + | clearcmdentry(); |
| return 0; | |
| } | |
| @@ -8395,42 +8445,28 @@ hashcd(void) | |
| * Called with interrupts off. | |
| */ | |
| static void FAST_FUNC | |
| - | changepath(const char *new) |
| + | changepath(const char *newval) |
| { | |
| - | const char *old; |
| - | int firstchange; |
| + | const char *new; |
| int idx; | |
| - | int idx_bltin; |
| + | int bltin; |
| - | old = pathval(); |
| - | firstchange = 9999; /* assume no change */ |
| + | new = newval; |
| idx = 0; | |
| - | idx_bltin = -1; |
| + | bltin = -1; |
| for (;;) { | |
| - | if (*old != *new) { |
| - | firstchange = idx; |
| - | if ((*old == '\0' && *new == ':') |
| - | || (*old == ':' && *new == '\0') |
| - | ) { |
| - | firstchange++; |
| - | } |
| - | old = new; /* ignore subsequent differences */ |
| + | if (*new == '%' && prefix(new + 1, "builtin")) { |
| + | bltin = idx; |
| + | break; |
| } | |
| - | if (*new == '\0') |
| + | new = strchr(new, ':'); |
| + | if (!new) |
| break; | |
| - | if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) |
| - | idx_bltin = idx; |
| - | if (*new == ':') |
| - | idx++; |
| + | idx++; |
| new++; | |
| - | old++; |
| } | |
| - | if (builtinloc < 0 && idx_bltin >= 0) |
| - | builtinloc = idx_bltin; /* zap builtins */ |
| - | if (builtinloc >= 0 && idx_bltin < 0) |
| - | firstchange = 0; |
| - | clearcmdentry(firstchange); |
| - | builtinloc = idx_bltin; |
| + | builtinloc = bltin; |
| + | clearcmdentry(); |
| } | |
| enum { | |
| TEOF, | |
| @@ -11024,7 +11060,7 @@ chkmail(void) | |
| for (;;) { | |
| int len; | |
| - | len = padvance(&mpath, nullstr); |
| + | len = padvance_magic(&mpath, nullstr, 2); |
| if (!len) | |
| break; | |
| p = stackblock(); | |
| @@ -13360,7 +13396,9 @@ find_dot_file(char *basename) | |
| while ((len = padvance(&path, basename)) >= 0) { | |
| fullname = stackblock(); | |
| - | if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { |
| + | if ((!pathopt || *pathopt == 'f') |
| + | && !stat(fullname, &statb) && S_ISREG(statb.st_mode) |
| + | ) { |
| /* This will be freed by the caller. */ | |
| return stalloc(len); | |
| } | |
| @@ -13566,17 +13604,19 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |
| idx = -1; | |
| loop: | |
| while ((len = padvance(&path, name)) >= 0) { | |
| + | const char *lpathopt = pathopt; |
| + | |
| fullname = stackblock(); | |
| idx++; | |
| - | if (pathopt) { |
| - | if (prefix(pathopt, "builtin")) { |
| + | if (lpathopt) { |
| + | if (*lpathopt == 'b') { |
| if (bcmd) | |
| goto builtin_success; | |
| continue; | |
| - | } |
| - | if ((act & DO_NOFUNC) |
| - | || !prefix(pathopt, "func") |
| - | ) { /* ignore unimplemented options */ |
| + | } else if (!(act & DO_NOFUNC)) { |
| + | /* handled below */ |
| + | } else { |
| + | /* ignore unimplemented options */ |
| continue; | |
| } | |
| } | |
| @@ -13599,7 +13639,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |
| e = EACCES; /* if we fail, this will be the error */ | |
| if (!S_ISREG(statb.st_mode)) | |
| continue; | |
| - | if (pathopt) { /* this is a %func directory */ |
| + | if (lpathopt) { /* this is a %func directory */ |
| stalloc(len); | |
| /* NB: stalloc will return space pointed by fullname | |
| * (because we don't have any intervening allocations | |