]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
tools: Fix stack overflow with many arguments (#2122)
authorTobias Stoeckmann <stoeckmann@users.noreply.github.com>
Sat, 13 Apr 2024 05:15:53 +0000 (05:15 +0000)
committerGitHub <noreply@github.com>
Sat, 13 Apr 2024 05:15:53 +0000 (22:15 -0700)
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.

cat/cmdline.c
cpio/cmdline.c
tar/cmdline.c
unzip/cmdline.c

index ea1e0eed6d0a4b932ee2f759022366a19580e8f3..851b63de06e5315b3b3e3e8530e351093a7af2c9 100644 (file)
@@ -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? */
index 312d762c8f46e695992cf534c3f6a4d6c8ebdafe..ab25492ede483b9867fa20953eb459a37f1e276f 100644 (file)
@@ -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? */
index 72292e8f27f395a963abb4745b599e1108ad9d28..2a89f42b08806dcd0aba3097657d783057ac6bde 100644 (file)
@@ -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? */
index ab1aeb31fe1878be5b28104bed06543073e9fd6b..4c6efc3e06944aedad7b886f2c59d62cb1bda9d8 100644 (file)
@@ -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? */