From: Tobias Stoeckmann Date: Sat, 13 Apr 2024 05:15:53 +0000 (+0000) Subject: tools: Fix stack overflow with many arguments (#2122) X-Git-Tag: v3.7.4~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bd974e1b7bac6f80e4a18f0dd261fa88d71eaa1c;p=thirdparty%2Flibarchive.git tools: Fix stack overflow with many arguments (#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. --- diff --git a/cat/cmdline.c b/cat/cmdline.c index ea1e0eed6..851b63de0 100644 --- a/cat/cmdline.c +++ b/cat/cmdline.c @@ -114,12 +114,18 @@ bsdcat_getopt(struct bsdcat *bsdcat) enum { state_start = 0, state_old_tar, state_next_word, state_short, state_long }; - const struct bsdcat_option *popt, *match = NULL, *match2 = NULL; - const char *p, *long_prefix = "--"; + const struct bsdcat_option *popt, *match, *match2; + const char *p, *long_prefix; size_t optlength; - int opt = '?'; - int required = 0; + int opt; + int required; +again: + match = NULL; + match2 = NULL; + long_prefix = "--"; + opt = '?'; + required = 0; bsdcat->argument = NULL; /* First time through, initialize everything. */ @@ -172,7 +178,7 @@ bsdcat_getopt(struct bsdcat *bsdcat) if (opt == '\0') { /* End of this group; recurse to get next option. */ bsdcat->getopt_state = state_next_word; - return bsdcat_getopt(bsdcat); + goto again; } /* Does this option take an argument? */ diff --git a/cpio/cmdline.c b/cpio/cmdline.c index 312d762c8..ab25492ed 100644 --- a/cpio/cmdline.c +++ b/cpio/cmdline.c @@ -114,12 +114,18 @@ cpio_getopt(struct cpio *cpio) static int state = state_start; static char *opt_word; - const struct option *popt, *match = NULL, *match2 = NULL; - const char *p, *long_prefix = "--"; + const struct option *popt, *match, *match2; + const char *p, *long_prefix; size_t optlength; - int opt = '?'; - int required = 0; + int opt; + int required; +again: + match = NULL; + match2 = NULL; + long_prefix = "--"; + opt = '?'; + required = 0; cpio->argument = NULL; /* First time through, initialize everything. */ @@ -169,7 +175,7 @@ cpio_getopt(struct cpio *cpio) if (opt == '\0') { /* End of this group; recurse to get next option. */ state = state_next_word; - return cpio_getopt(cpio); + goto again; } /* Does this option take an argument? */ diff --git a/tar/cmdline.c b/tar/cmdline.c index 72292e8f2..2a89f42b0 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -218,12 +218,18 @@ bsdtar_getopt(struct bsdtar *bsdtar) enum { state_start = 0, state_old_tar, state_next_word, state_short, state_long }; - const struct bsdtar_option *popt, *match = NULL, *match2 = NULL; - const char *p, *long_prefix = "--"; + const struct bsdtar_option *popt, *match, *match2; + const char *p, *long_prefix; size_t optlength; - int opt = '?'; - int required = 0; + int opt; + int required; +again: + match = NULL; + match2 = NULL; + long_prefix = "--"; + opt = '?'; + required = 0; bsdtar->argument = NULL; /* First time through, initialize everything. */ @@ -310,7 +316,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) if (opt == '\0') { /* End of this group; recurse to get next option. */ bsdtar->getopt_state = state_next_word; - return bsdtar_getopt(bsdtar); + goto again; } /* Does this option take an argument? */ diff --git a/unzip/cmdline.c b/unzip/cmdline.c index ab1aeb31f..4c6efc3e0 100644 --- a/unzip/cmdline.c +++ b/unzip/cmdline.c @@ -81,12 +81,18 @@ bsdunzip_getopt(struct bsdunzip *bsdunzip) { enum { state_start = 0, state_next_word, state_short, state_long }; - const struct bsdunzip_option *popt, *match = NULL, *match2 = NULL; - const char *p, *long_prefix = "--"; + const struct bsdunzip_option *popt, *match, *match2; + const char *p, *long_prefix; size_t optlength; - int opt = OPTION_NONE; - int required = 0; - + int opt; + int required; + +again: + match = NULL; + match2 = NULL; + long_prefix = "--"; + opt = OPTION_NONE; + required = 0; bsdunzip->argument = NULL; /* First time through, initialize everything. */ @@ -140,7 +146,7 @@ bsdunzip_getopt(struct bsdunzip *bsdunzip) if (opt == '\0') { /* End of this group; recurse to get next option. */ bsdunzip->getopt_state = state_next_word; - return bsdunzip_getopt(bsdunzip); + goto again; } /* Does this option take an argument? */