]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
archive_match: Prevent call stack overflow
authorTobias Stoeckmann <tobias@stoeckmann.org>
Wed, 18 Mar 2026 13:57:43 +0000 (14:57 +0100)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Sat, 9 May 2026 10:18:38 +0000 (12:18 +0200)
Patterns with a lot of asterisks may overflow the call stack, crashing
the application. Check the recursion depth. If it is too deep, fail
with an error.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
libarchive/archive_match.c
libarchive/archive_pathmatch.c

index 369f4e7c8188e480cb3f564b5b0e4f571d7fdcf1..c40a043ad2a3a1fb83937cdb1089203ddcfcec20 100644 (file)
@@ -216,6 +216,14 @@ error_nomem(struct archive_match *a)
        return (ARCHIVE_FATAL);
 }
 
+static int
+error_pattern(struct archive_match *a)
+{
+       archive_set_error(&(a->archive), EINVAL, "Failed to apply pattern");
+       a->archive.state = ARCHIVE_STATE_FATAL;
+       return (ARCHIVE_FATAL);
+}
+
 /*
  * Create an ARCHIVE_MATCH object.
  */
@@ -293,6 +301,8 @@ archive_match_excluded(struct archive *_a, struct archive_entry *entry)
 #else
                r = path_excluded(a, 1, archive_entry_pathname(entry));
 #endif
+               if (r < 0)
+                       return (error_pattern(a));
                if (r != 0)
                        return (r);
        }
@@ -456,6 +466,7 @@ archive_match_path_excluded(struct archive *_a,
     struct archive_entry *entry)
 {
        struct archive_match *a;
+       int r;
 
        archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
            ARCHIVE_STATE_NEW, "archive_match_path_excluded");
@@ -471,10 +482,13 @@ archive_match_path_excluded(struct archive *_a,
        if ((a->setflag & PATTERN_IS_SET) == 0)
                return (0);
 #if defined(_WIN32) && !defined(__CYGWIN__)
-       return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+       r = path_excluded(a, 0, archive_entry_pathname_w(entry));
 #else
-       return (path_excluded(a, 1, archive_entry_pathname(entry)));
+       r = path_excluded(a, 1, archive_entry_pathname(entry));
 #endif
+       if (r < 0)
+               return (error_pattern(a));
+       return (r);
 }
 
 /*
index 9ea13f7c6de7b754588670854f88646f6e930b05..f65f43745527f70697539ae791dcd963ece5e79c 100644 (file)
@@ -35,6 +35,8 @@
 
 #include "archive_pathmatch.h"
 
+#define MAX_RECURSION  100
+
 /*
  * Check whether a character 'c' is matched by a list specification [...]:
  *    * Leading '!' or '^' negates the class.
@@ -167,9 +169,13 @@ pm_slashskip_w(const wchar_t *s) {
 }
 
 static int
-pm(const char *p, const char *s, int flags)
+pm(const char *p, const char *s, int flags, int depth)
 {
        const char *end;
+       int r;
+
+       if (depth > MAX_RECURSION)
+               return (-1);
 
        /*
         * Ignore leading './', './/', '././', etc.
@@ -202,8 +208,9 @@ pm(const char *p, const char *s, int flags)
                        if (*p == '\0')
                                return (1);
                        while (*s) {
-                               if (pm(p, s, flags))
-                                       return (1);
+                               r = pm(p, s, flags, depth + 1);
+                               if (r)
+                                       return (r);
                                ++s;
                        }
                        return (0);
@@ -272,9 +279,13 @@ pm(const char *p, const char *s, int flags)
 }
 
 static int
-pm_w(const wchar_t *p, const wchar_t *s, int flags)
+pm_w(const wchar_t *p, const wchar_t *s, int flags, int depth)
 {
        const wchar_t *end;
+       int r;
+
+       if (depth > MAX_RECURSION)
+               return (-1);
 
        /*
         * Ignore leading './', './/', '././', etc.
@@ -307,8 +318,9 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags)
                        if (*p == L'\0')
                                return (1);
                        while (*s) {
-                               if (pm_w(p, s, flags))
-                                       return (1);
+                               r = pm_w(p, s, flags, depth + 1);
+                               if (r)
+                                       return (r);
                                ++s;
                        }
                        return (0);
@@ -401,22 +413,25 @@ __archive_pathmatch(const char *p, const char *s, int flags)
                        ++p;
                while (*s == '/')
                        ++s;
-               return (pm(p, s, flags));
+               return (pm(p, s, flags, 0));
        }
 
        /* If start is unanchored, try to match start of each path element. */
        if (flags & PATHMATCH_NO_ANCHOR_START) {
                for ( ; s != NULL; s = strchr(s, '/')) {
+                       int r;
+
                        if (*s == '/')
                                s++;
-                       if (pm(p, s, flags))
-                               return (1);
+                       r = pm(p, s, flags, 0);
+                       if (r)
+                               return (r);
                }
                return (0);
        }
 
        /* Default: Match from beginning. */
-       return (pm(p, s, flags));
+       return (pm(p, s, flags, 0));
 }
 
 int
@@ -443,20 +458,23 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
                        ++p;
                while (*s == L'/')
                        ++s;
-               return (pm_w(p, s, flags));
+               return (pm_w(p, s, flags, 0));
        }
 
        /* If start is unanchored, try to match start of each path element. */
        if (flags & PATHMATCH_NO_ANCHOR_START) {
                for ( ; s != NULL; s = wcschr(s, L'/')) {
+                       int r;
+
                        if (*s == L'/')
                                s++;
-                       if (pm_w(p, s, flags))
-                               return (1);
+                       r = pm_w(p, s, flags, 0);
+                       if (r)
+                               return (r);
                }
                return (0);
        }
 
        /* Default: Match from beginning. */
-       return (pm_w(p, s, flags));
+       return (pm_w(p, s, flags, 0));
 }