From: Lennart Poettering Date: Mon, 19 May 2025 10:58:52 +0000 (+0200) Subject: path-util: add flavour of path_startswith() that leaves a leading slash in place X-Git-Tag: archive/raspbian/252.39-1_deb12u2+rpi1^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=e33c14f2554f0c603c631755e513cbd7d323e6a6;p=systemd.git path-util: add flavour of path_startswith() that leaves a leading slash in place (cherry picked from commit ee19edbb9f3455db3f750089082f3e5a925e3a0c) Origin: backport, https://github.com/systemd/systemd/commit/20021e7686426052e3a7505425d7e12085feb2a6 Gbp-Pq: Name CVE-2026-29111-1.patch --- diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d71c07c7..9950ff3e 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -67,7 +67,7 @@ int rmdir_parents(const char *path, const char *stop) { assert(*slash == '/'); *slash = '\0'; - if (path_startswith_full(stop, p, /* accept_dot_dot= */ false)) + if (path_startswith_full(stop, p, /* flags= */ 0)) return 0; if (rmdir(p) < 0 && errno != ENOENT) diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index c8ff342d..478cb693 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -99,7 +99,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui assert(_mkdirat != mkdirat); if (prefix) { - p = path_startswith_full(path, prefix, /* accept_dot_dot= */ false); + p = path_startswith_full(path, prefix, /* flags= */ 0); if (!p) return -ENOTDIR; } else @@ -144,7 +144,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui s[n] = '\0'; - if (!prefix || !path_startswith_full(prefix, path, /* accept_dot_dot= */ false)) { + if (!prefix || !path_startswith_full(prefix, path, /* flags= */ 0)) { r = mkdir_safe_internal(path, mode, uid, gid, flags | MKDIR_IGNORE_EXISTING, _mkdirat); if (r < 0 && r != -EEXIST) return r; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 72c0d6ba..0c2d08c6 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -399,8 +399,8 @@ char* path_simplify_full(char *path, PathSimplifyFlags flags) { return path; } -char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) { - assert(path); +char* path_startswith_full(const char *original_path, const char *prefix, PathStartWithFlags flags) { + assert(original_path); assert(prefix); /* Returns a pointer to the start of the first component after the parts matched by @@ -413,28 +413,45 @@ char* path_startswith_full(const char *path, const char *prefix, bool accept_dot * Returns NULL otherwise. */ + const char *path = original_path; + if ((path[0] == '/') != (prefix[0] == '/')) return NULL; for (;;) { const char *p, *q; - int r, k; + int m, n; - r = path_find_first_component(&path, accept_dot_dot, &p); - if (r < 0) + m = path_find_first_component(&path, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &p); + if (m < 0) return NULL; - k = path_find_first_component(&prefix, accept_dot_dot, &q); - if (k < 0) + n = path_find_first_component(&prefix, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &q); + if (n < 0) return NULL; - if (k == 0) - return (char*) (p ?: path); + if (n == 0) { + if (!p) + p = path; + + if (FLAGS_SET(flags, PATH_STARTSWITH_RETURN_LEADING_SLASH)) { + + if (p <= original_path) + return NULL; + + p--; + + if (*p != '/') + return NULL; + } + + return (char*) p; + } - if (r != k) + if (m != n) return NULL; - if (!strneq(p, q, r)) + if (!strneq(p, q, m)) return NULL; } } diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 09604ba4..e083a91a 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -62,9 +62,15 @@ int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from, const char *to, char **ret); int path_make_relative_parent(const char *from_child, const char *to, char **ret); -char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; + +typedef enum PathStartWithFlags { + PATH_STARTSWITH_ACCEPT_DOT_DOT = 1U << 0, + PATH_STARTSWITH_RETURN_LEADING_SLASH = 1U << 1, +} PathStartWithFlags; + +char* path_startswith_full(const char *path, const char *prefix, PathStartWithFlags flags) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { - return path_startswith_full(path, prefix, true); + return path_startswith_full(path, prefix, PATH_STARTSWITH_ACCEPT_DOT_DOT); } int path_compare(const char *a, const char *b) _pure_; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 7ab9f20b..6e495e6b 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -671,6 +671,22 @@ TEST(path_startswith) { test_path_startswith_one("/foo/bar/barfoo/", "/fo", NULL, NULL); } +static void test_path_startswith_return_leading_slash_one(const char *path, const char *prefix, const char *expected) { + const char *p; + + log_debug("/* %s(%s, %s) */", __func__, path, prefix); + + p = path_startswith_full(path, prefix, PATH_STARTSWITH_RETURN_LEADING_SLASH); + assert_se(streq_ptr(p, expected)); +} + +TEST(path_startswith_return_leading_slash) { + test_path_startswith_return_leading_slash_one("/foo/bar", "/", "/foo/bar"); + test_path_startswith_return_leading_slash_one("/foo/bar", "/foo", "/bar"); + test_path_startswith_return_leading_slash_one("/foo/bar", "/foo/bar", NULL); + test_path_startswith_return_leading_slash_one("/foo/bar/", "/foo/bar", "/"); +} + static void test_prefix_root_one(const char *r, const char *p, const char *expected) { _cleanup_free_ char *s = NULL; const char *t;