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.
This commit is contained in:
parent
fd86d88034
commit
1bc2fdfabf
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
};
|
||||
|
9
bin/sh/tests/builtins/getopts9.0
Normal file
9
bin/sh/tests/builtins/getopts9.0
Normal file
@ -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
|
3
bin/sh/tests/builtins/getopts9.0.stdout
Normal file
3
bin/sh/tests/builtins/getopts9.0.stdout
Normal file
@ -0,0 +1,3 @@
|
||||
0:a:
|
||||
0:b:
|
||||
1:?:
|
Loading…
Reference in New Issue
Block a user