diff options
author | Jilles Tjoelker <jilles@FreeBSD.org> | 2014-10-26 17:50:33 +0000 |
---|---|---|
committer | Jilles Tjoelker <jilles@FreeBSD.org> | 2014-10-26 17:50:33 +0000 |
commit | 1bc2fdfabf8efeb2762cc14ecb24da25ea78653e (patch) | |
tree | 8484fb30e43bfd82dc96d44ef76a402bd991793c /bin | |
parent | fd86d88034ee7491e7e2971fe699581d3641dab0 (diff) | |
download | src-1bc2fdfabf8efeb2762cc14ecb24da25ea78653e.tar.gz src-1bc2fdfabf8efeb2762cc14ecb24da25ea78653e.zip |
sh: Make getopts memory-safe if with changing arguments.
POSIX does not permit to continuing a getopts loop with different
arguments. For parsing the positional parameters, we handle this case by
resetting the getopts state when the positional parameters are changed in
any way (and the getopts state is local to a function). However, in the
syntax getopts <optstring> <var> <arg...>, changes could lead to invalid
memory access.
In the syntax getopts <optstring> <var> <arg...>, store a copy of the
arguments and continue to use them until getopts is reset.
Notes
Notes:
svn path=/head/; revision=273700
Diffstat (limited to 'bin')
-rw-r--r-- | bin/sh/eval.c | 1 | ||||
-rw-r--r-- | bin/sh/options.c | 31 | ||||
-rw-r--r-- | bin/sh/options.h | 1 | ||||
-rw-r--r-- | bin/sh/tests/builtins/getopts9.0 | 9 | ||||
-rw-r--r-- | bin/sh/tests/builtins/getopts9.0.stdout | 3 |
5 files changed, 39 insertions, 6 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c index 38e227c7d74d..c1a9cdbfcd93 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -1039,6 +1039,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; + shellparam.optp = NULL; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; diff --git a/bin/sh/options.c b/bin/sh/options.c index bf00a4e7d47e..860cf6c645bb 100644 --- a/bin/sh/options.c +++ b/bin/sh/options.c @@ -325,6 +325,7 @@ setparam(char **argv) shellparam.malloc = 1; shellparam.nparam = nparam; shellparam.p = newparam; + shellparam.optp = NULL; shellparam.reset = 1; shellparam.optnext = NULL; } @@ -344,6 +345,11 @@ freeparam(struct shparam *param) ckfree(*ap); ckfree(param->p); } + if (param->optp) { + for (ap = param->optp ; *ap ; ap++) + ckfree(*ap); + ckfree(param->optp); + } } @@ -417,20 +423,33 @@ getoptsreset(const char *value) int getoptscmd(int argc, char **argv) { - char **optbase = NULL; + char **optbase = NULL, **ap; + int i; if (argc < 3) error("usage: getopts optstring var [arg]"); - else if (argc == 3) - optbase = shellparam.p; - else - optbase = &argv[3]; if (shellparam.reset == 1) { + INTOFF; + if (shellparam.optp) { + for (ap = shellparam.optp ; *ap ; ap++) + ckfree(*ap); + ckfree(shellparam.optp); + shellparam.optp = NULL; + } + if (argc > 3) { + shellparam.optp = ckmalloc((argc - 2) * sizeof *ap); + memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap); + for (i = 0; i < argc - 3; i++) + shellparam.optp[i] = savestr(argv[i + 3]); + } + INTON; + optbase = argc == 3 ? shellparam.p : shellparam.optp; shellparam.optnext = optbase; shellparam.optptr = NULL; shellparam.reset = 0; - } + } else + optbase = shellparam.optp ? shellparam.optp : shellparam.p; return getopts(argv[1], argv[2], optbase, &shellparam.optnext, &shellparam.optptr); diff --git a/bin/sh/options.h b/bin/sh/options.h index acc2a1131c28..09948628197d 100644 --- a/bin/sh/options.h +++ b/bin/sh/options.h @@ -38,6 +38,7 @@ struct shparam { unsigned char malloc; /* if parameter list dynamically allocated */ unsigned char reset; /* if getopts has been reset */ char **p; /* parameter list */ + char **optp; /* parameter list for getopts */ char **optnext; /* next parameter to be processed by getopts */ char *optptr; /* used by getopts */ }; diff --git a/bin/sh/tests/builtins/getopts9.0 b/bin/sh/tests/builtins/getopts9.0 new file mode 100644 index 000000000000..d23fc432af97 --- /dev/null +++ b/bin/sh/tests/builtins/getopts9.0 @@ -0,0 +1,9 @@ +# $FreeBSD$ + +args='-ab' +getopts ab opt $args +echo $?:$opt:$OPTARG +for dummy in dummy1 dummy2; do + getopts ab opt $args + echo $?:$opt:$OPTARG +done diff --git a/bin/sh/tests/builtins/getopts9.0.stdout b/bin/sh/tests/builtins/getopts9.0.stdout new file mode 100644 index 000000000000..4d32063faa4d --- /dev/null +++ b/bin/sh/tests/builtins/getopts9.0.stdout @@ -0,0 +1,3 @@ +0:a: +0:b: +1:?: |