diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-20 07:52:33 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-20 07:52:33 +0000 |
commit | d1660cb9ad3adb4b99c098de88f79cbeb74c3a5d (patch) | |
tree | cddc476b03fc1eb2eb95f16399b55348b306885e | |
parent | 802a7be54ffcf6e45808d72e2562456bba564028 (diff) | |
download | busybox-w32-d1660cb9ad3adb4b99c098de88f79cbeb74c3a5d.tar.gz busybox-w32-d1660cb9ad3adb4b99c098de88f79cbeb74c3a5d.tar.bz2 busybox-w32-d1660cb9ad3adb4b99c098de88f79cbeb74c3a5d.zip |
ash: fix a bug in standalone mode (corrupted getopt() state)
-rw-r--r-- | libbb/getopt32.c | 22 | ||||
-rw-r--r-- | libbb/vfork_daemon_rexec.c | 36 | ||||
-rw-r--r-- | shell/ash_test/ash-standalone/nofork_trashes_getopt.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-standalone/nofork_trashes_getopt.tests | 6 | ||||
-rw-r--r-- | util-linux/getopt.c | 11 |
5 files changed, 43 insertions, 33 deletions
diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 43fb6eb86..49fb5335d 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c | |||
@@ -515,28 +515,6 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
515 | } | 515 | } |
516 | } | 516 | } |
517 | 517 | ||
518 | /* In case getopt32 was already called: | ||
519 | * reset the libc getopt() function, which keeps internal state. | ||
520 | * | ||
521 | * BSD-derived getopt() functions require that optind be set to 1 in | ||
522 | * order to reset getopt() state. This used to be generally accepted | ||
523 | * way of resetting getopt(). However, glibc's getopt() | ||
524 | * has additional getopt() state beyond optind, and requires that | ||
525 | * optind be set to zero to reset its state. So the unfortunate state of | ||
526 | * affairs is that BSD-derived versions of getopt() misbehave if | ||
527 | * optind is set to 0 in order to reset getopt(), and glibc's getopt() | ||
528 | * will core dump if optind is set 1 in order to reset getopt(). | ||
529 | * | ||
530 | * More modern versions of BSD require that optreset be set to 1 in | ||
531 | * order to reset getopt(). Sigh. Standards, anyone? | ||
532 | */ | ||
533 | #ifdef __GLIBC__ | ||
534 | optind = 0; | ||
535 | #else /* BSD style */ | ||
536 | optind = 1; | ||
537 | /* optreset = 1; */ | ||
538 | #endif | ||
539 | /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ | ||
540 | pargv = NULL; | 518 | pargv = NULL; |
541 | 519 | ||
542 | /* Note: just "getopt() <= 0" will not work well for | 520 | /* Note: just "getopt() <= 0" will not work well for |
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index da0dc03e5..17b373cd1 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c | |||
@@ -125,6 +125,7 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n | |||
125 | int rc, argc; | 125 | int rc, argc; |
126 | 126 | ||
127 | applet_name = APPLET_NAME(applet_no); | 127 | applet_name = APPLET_NAME(applet_no); |
128 | |||
128 | xfunc_error_retval = EXIT_FAILURE; | 129 | xfunc_error_retval = EXIT_FAILURE; |
129 | 130 | ||
130 | /* Special flag for xfunc_die(). If xfunc will "die" | 131 | /* Special flag for xfunc_die(). If xfunc will "die" |
@@ -132,7 +133,30 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n | |||
132 | * die_sleep and longjmp here instead. */ | 133 | * die_sleep and longjmp here instead. */ |
133 | die_sleep = -1; | 134 | die_sleep = -1; |
134 | 135 | ||
135 | /* option_mask32 = 0; - not needed */ | 136 | /* In case getopt() or getopt32() was already called: |
137 | * reset the libc getopt() function, which keeps internal state. | ||
138 | * | ||
139 | * BSD-derived getopt() functions require that optind be set to 1 in | ||
140 | * order to reset getopt() state. This used to be generally accepted | ||
141 | * way of resetting getopt(). However, glibc's getopt() | ||
142 | * has additional getopt() state beyond optind, and requires that | ||
143 | * optind be set to zero to reset its state. So the unfortunate state of | ||
144 | * affairs is that BSD-derived versions of getopt() misbehave if | ||
145 | * optind is set to 0 in order to reset getopt(), and glibc's getopt() | ||
146 | * will core dump if optind is set 1 in order to reset getopt(). | ||
147 | * | ||
148 | * More modern versions of BSD require that optreset be set to 1 in | ||
149 | * order to reset getopt(). Sigh. Standards, anyone? | ||
150 | */ | ||
151 | #ifdef __GLIBC__ | ||
152 | optind = 0; | ||
153 | #else /* BSD style */ | ||
154 | optind = 1; | ||
155 | /* optreset = 1; */ | ||
156 | #endif | ||
157 | /* optarg = NULL; opterr = 1; optopt = 63; - do we need this too? */ | ||
158 | /* (values above are what they initialized to in glibc and uclibc) */ | ||
159 | /* option_mask32 = 0; - not needed, no applet depends on it being 0 */ | ||
136 | 160 | ||
137 | argc = 1; | 161 | argc = 1; |
138 | while (argv[argc]) | 162 | while (argv[argc]) |
@@ -161,8 +185,16 @@ int FAST_FUNC run_nofork_applet_prime(struct nofork_save_area *old, int applet_n | |||
161 | rc = 0; | 185 | rc = 0; |
162 | } | 186 | } |
163 | 187 | ||
164 | /* Restoring globals */ | 188 | /* Restoring some globals */ |
165 | restore_nofork_data(old); | 189 | restore_nofork_data(old); |
190 | |||
191 | /* Other globals can be simply reset to defaults */ | ||
192 | #ifdef __GLIBC__ | ||
193 | optind = 0; | ||
194 | #else /* BSD style */ | ||
195 | optind = 1; | ||
196 | #endif | ||
197 | |||
166 | return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ | 198 | return rc & 0xff; /* don't confuse people with "exitcodes" >255 */ |
167 | } | 199 | } |
168 | 200 | ||
diff --git a/shell/ash_test/ash-standalone/nofork_trashes_getopt.right b/shell/ash_test/ash-standalone/nofork_trashes_getopt.right new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_trashes_getopt.right | |||
@@ -0,0 +1 @@ | |||
0 | |||
diff --git a/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests b/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests new file mode 100755 index 000000000..f42c50730 --- /dev/null +++ b/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # In this test, rm is NOFORK and it modifies getopt internal state | ||
2 | rm -f non_existent_file | ||
3 | # Subsequent hexdump is run as NOEXEC, and thus still uses this state | ||
4 | hexdump </dev/null | ||
5 | # Did hexdump segfault etc? | ||
6 | echo $? | ||
diff --git a/util-linux/getopt.c b/util-linux/getopt.c index 402630385..8b5e46c7b 100644 --- a/util-linux/getopt.c +++ b/util-linux/getopt.c | |||
@@ -142,7 +142,8 @@ static const char *normalize(const char *arg) | |||
142 | * Other settings are found in global variables. | 142 | * Other settings are found in global variables. |
143 | */ | 143 | */ |
144 | #if !ENABLE_GETOPT_LONG | 144 | #if !ENABLE_GETOPT_LONG |
145 | #define generate_output(argv,argc,optstr,longopts) generate_output(argv,argc,optstr) | 145 | #define generate_output(argv,argc,optstr,longopts) \ |
146 | generate_output(argv,argc,optstr) | ||
146 | #endif | 147 | #endif |
147 | static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts) | 148 | static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts) |
148 | { | 149 | { |
@@ -156,14 +157,6 @@ static int generate_output(char **argv, int argc, const char *optstr, const stru | |||
156 | if (quiet_errors) /* No error reporting from getopt(3) */ | 157 | if (quiet_errors) /* No error reporting from getopt(3) */ |
157 | opterr = 0; | 158 | opterr = 0; |
158 | 159 | ||
159 | /* Reset getopt(3) (see libbb/getopt32.c for long rant) */ | ||
160 | #ifdef __GLIBC__ | ||
161 | optind = 0; | ||
162 | #else /* BSD style */ | ||
163 | optind = 1; | ||
164 | /* optreset = 1; */ | ||
165 | #endif | ||
166 | |||
167 | while (1) { | 160 | while (1) { |
168 | opt = | 161 | opt = |
169 | #if ENABLE_GETOPT_LONG | 162 | #if ENABLE_GETOPT_LONG |