Skip to content

Commit bd974e1

Browse files
authored
tools: Fix stack overflow with many arguments (libarchive#2122)
Supplying a lot of "-" arguments to tools can lead to stack overflow due to recursive *_getopt function calls. Proof of Concept: 1. Compile libarchive with Visual Studio 2022 2. Call bsdtar with insane amount of arguments ``` PS> bsdtar.exe ("- "*10000).split(" ") ``` The event log shows that bsdtar.exe failed with `0xc00000fd` (stack overflow). If compiled with gcc, this does not happen by default because the code is internally optimized to use this suggested loop instead. You have to compile with CFLAGS="-O0" to provoke it with gcc as well.
1 parent 7bc8531 commit bd974e1

4 files changed

Lines changed: 45 additions & 21 deletions

File tree

cat/cmdline.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,18 @@ bsdcat_getopt(struct bsdcat *bsdcat)
114114
enum { state_start = 0, state_old_tar, state_next_word,
115115
state_short, state_long };
116116

117-
const struct bsdcat_option *popt, *match = NULL, *match2 = NULL;
118-
const char *p, *long_prefix = "--";
117+
const struct bsdcat_option *popt, *match, *match2;
118+
const char *p, *long_prefix;
119119
size_t optlength;
120-
int opt = '?';
121-
int required = 0;
120+
int opt;
121+
int required;
122122

123+
again:
124+
match = NULL;
125+
match2 = NULL;
126+
long_prefix = "--";
127+
opt = '?';
128+
required = 0;
123129
bsdcat->argument = NULL;
124130

125131
/* First time through, initialize everything. */
@@ -172,7 +178,7 @@ bsdcat_getopt(struct bsdcat *bsdcat)
172178
if (opt == '\0') {
173179
/* End of this group; recurse to get next option. */
174180
bsdcat->getopt_state = state_next_word;
175-
return bsdcat_getopt(bsdcat);
181+
goto again;
176182
}
177183

178184
/* Does this option take an argument? */

cpio/cmdline.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,18 @@ cpio_getopt(struct cpio *cpio)
114114
static int state = state_start;
115115
static char *opt_word;
116116

117-
const struct option *popt, *match = NULL, *match2 = NULL;
118-
const char *p, *long_prefix = "--";
117+
const struct option *popt, *match, *match2;
118+
const char *p, *long_prefix;
119119
size_t optlength;
120-
int opt = '?';
121-
int required = 0;
120+
int opt;
121+
int required;
122122

123+
again:
124+
match = NULL;
125+
match2 = NULL;
126+
long_prefix = "--";
127+
opt = '?';
128+
required = 0;
123129
cpio->argument = NULL;
124130

125131
/* First time through, initialize everything. */
@@ -169,7 +175,7 @@ cpio_getopt(struct cpio *cpio)
169175
if (opt == '\0') {
170176
/* End of this group; recurse to get next option. */
171177
state = state_next_word;
172-
return cpio_getopt(cpio);
178+
goto again;
173179
}
174180

175181
/* Does this option take an argument? */

tar/cmdline.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,18 @@ bsdtar_getopt(struct bsdtar *bsdtar)
218218
enum { state_start = 0, state_old_tar, state_next_word,
219219
state_short, state_long };
220220

221-
const struct bsdtar_option *popt, *match = NULL, *match2 = NULL;
222-
const char *p, *long_prefix = "--";
221+
const struct bsdtar_option *popt, *match, *match2;
222+
const char *p, *long_prefix;
223223
size_t optlength;
224-
int opt = '?';
225-
int required = 0;
224+
int opt;
225+
int required;
226226

227+
again:
228+
match = NULL;
229+
match2 = NULL;
230+
long_prefix = "--";
231+
opt = '?';
232+
required = 0;
227233
bsdtar->argument = NULL;
228234

229235
/* First time through, initialize everything. */
@@ -310,7 +316,7 @@ bsdtar_getopt(struct bsdtar *bsdtar)
310316
if (opt == '\0') {
311317
/* End of this group; recurse to get next option. */
312318
bsdtar->getopt_state = state_next_word;
313-
return bsdtar_getopt(bsdtar);
319+
goto again;
314320
}
315321

316322
/* Does this option take an argument? */

unzip/cmdline.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ bsdunzip_getopt(struct bsdunzip *bsdunzip)
8181
{
8282
enum { state_start = 0, state_next_word, state_short, state_long };
8383

84-
const struct bsdunzip_option *popt, *match = NULL, *match2 = NULL;
85-
const char *p, *long_prefix = "--";
84+
const struct bsdunzip_option *popt, *match, *match2;
85+
const char *p, *long_prefix;
8686
size_t optlength;
87-
int opt = OPTION_NONE;
88-
int required = 0;
89-
87+
int opt;
88+
int required;
89+
90+
again:
91+
match = NULL;
92+
match2 = NULL;
93+
long_prefix = "--";
94+
opt = OPTION_NONE;
95+
required = 0;
9096
bsdunzip->argument = NULL;
9197

9298
/* First time through, initialize everything. */
@@ -140,7 +146,7 @@ bsdunzip_getopt(struct bsdunzip *bsdunzip)
140146
if (opt == '\0') {
141147
/* End of this group; recurse to get next option. */
142148
bsdunzip->getopt_state = state_next_word;
143-
return bsdunzip_getopt(bsdunzip);
149+
goto again;
144150
}
145151

146152
/* Does this option take an argument? */

0 commit comments

Comments
 (0)