On Sun, Mar 15, 2020 at 12:21:02PM +0200, 0xef967...@gmail.com wrote: > On Sun, Mar 15, 2020 at 09:30:22AM +0100, Martijn van Duren wrote: > > > --- lib/libc/stdlib/getopt_long.c~ > > > +++ lib/libc/stdlib/getopt_long.c > > > @@ -418,15 +418,8 @@ > > > } > > > > > > if ((optchar = (int)*place++) == (int)':' || > > > - (optchar == (int)'-' && *place != '\0') || > > > - (oli = strchr(options, optchar)) == NULL) { > > > - /* > > > - * If the user specified "-" and '-' isn't listed in > > > - * options, return -1 (non-option) as per POSIX. > > > - * Otherwise, it is an unknown option character (or ':'). > > > - */ > > > - if (optchar == (int)'-' && *place == '\0') > > > - return (-1); > > > + ((oli = strchr(options, optchar)) == NULL || > > > + (optchar == (int)'-' && oli == options))) { > > > if (!*place) > > > ++optind; > > > if (PRINT_ERROR)
A "side-effect" of my patch is that it prevents the only case where OpenBSD's getopt(3) was clinging to the stale state after having finished parsing the arguments and returned -1. The patch restore the older behaviour where you could just set "optind = 1" in order to parse another list of arguments (instead of the unportable "optreset = 1" or "optind = 0"), without exposing you to crashes and possible exploits. That used to work in the old getopt(3) from *BSD, and in the (original?) SysV version, but was broken in the glibc version of getopt(3) and, in a corner case, in the getopt_long implementation from OpenBSD. You could test it with the getopt-test-relocate.c program at the end of this message. More info at https://stackoverflow.com/questions/60483737, and a glibc patch at https://sourceware.org/bugzilla/show_bug.cgi?id=25658 OPTS=q ./getopt-test-relocate-unpatched -q- {./getopt-test-relocate-unpatched} <q> | {-q-} getopt-test-relocate-unpatched: unknown option -- : getopt-test-relocate-unpatched: unknown option -- O getopt-test-relocate-unpatched: unknown option -- P getopt-test-relocate-unpatched: unknown option -- T getopt-test-relocate-unpatched: unknown option -- S getopt-test-relocate-unpatched: unknown option -- = {./getopt-test-relocate-unpatched} #U: #UO #UP #UT #US #U= <q> | ===================================================================== getopt-test-relocate.c ===================================================================== #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> static int showargs(int ac, char **av, char *os){ char *s; int c, i; if(ac < 1) return 1; printf("{%s}", av[0]); while((c = getopt(ac, av, os)) != -1) switch(c){ case '?': printf(" #U%c", optopt); break; case ':': printf(" #A%c", optopt); break; case '\1': printf(" <\\1>={%s}", optarg); break; default: if((s = strrchr(os, c)) && s[1] == ':') printf(" <%c>={%s}", c, optarg); else printf(" <%c>", c); } printf(" |"); for(i = optind; i < ac; i++) printf(" {%s}", av[i]); printf("\n"); return 0; } static void relocate_args(int ac, char **av){ int i; for(i = 1; i < ac; i++){ char *o = av[i]; size_t l = strlen(o); if(!(av[i] = strdup(o))) abort(); memset(o, ':', l + 1); } } int main(int ac, char **av){ char *os; if(!(os = getenv("OPTS"))) os = ""; showargs(ac, av, os); relocate_args(ac, av); optind = 1; return showargs(ac, av, os); }