From 27a87dee8b5d705945944a0b83a6eea1a6e238f1 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Thu, 12 Nov 2026 14:24:46 -0300 Subject: [PATCH] reject embedded NULs in paths --- mongoose.c | 12 ++++++++---- src/http.c | 12 ++++++++---- test/unit_test.c | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/mongoose.c b/mongoose.c index af9258d5..79213c67 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4906,11 +4906,15 @@ static bool uri2path(const char *r, struct mg_str uri, char *buf, size_t len) { n = mg_snprintf(buf, len, "%.*s", (int) d.len, d.buf); if (len == 0 || n + 2 >= len) return false; // Path overflow if (n > 0 && buf[n - 1] != '/') buf[n++] = '/', buf[n] = '\0'; // Add slash - if (mg_url_decode(uri.buf + u.len, uri.len - u.len, buf + n, len - n, 0) < 0) { - return false; + { + int dn; + size_t p = n; + dn = mg_url_decode(uri.buf + u.len, uri.len - u.len, buf + n, len - n, 0); + if (dn < 0) return false; // reject on malformed + buf[len - 1] = '\0'; // Double-check + n = strlen(buf); + if (n != p + (size_t) dn) return false; // reject on embedded NULs } - buf[len - 1] = '\0'; // Double-check - n = strlen(buf); if (!mg_path_is_sane(mg_str_n(buf, n))) return false; while (n > 1 && buf[n - 1] == '/') buf[--n] = 0; // Trim trailing slashes return true; diff --git a/src/http.c b/src/http.c index eae3bc4a..155f7c64 100644 --- a/src/http.c +++ b/src/http.c @@ -752,11 +752,15 @@ static bool uri2path(const char *r, struct mg_str uri, char *buf, size_t len) { n = mg_snprintf(buf, len, "%.*s", (int) d.len, d.buf); if (len == 0 || n + 2 >= len) return false; // Path overflow if (n > 0 && buf[n - 1] != '/') buf[n++] = '/', buf[n] = '\0'; // Add slash - if (mg_url_decode(uri.buf + u.len, uri.len - u.len, buf + n, len - n, 0) < 0) { - return false; + { + int dn; + size_t p = n; + dn = mg_url_decode(uri.buf + u.len, uri.len - u.len, buf + n, len - n, 0); + if (dn < 0) return false; // reject on malformed + buf[len - 1] = '\0'; // Double-check + n = strlen(buf); + if (n != p + (size_t) dn) return false; // reject on embedded NULs } - buf[len - 1] = '\0'; // Double-check - n = strlen(buf); if (!mg_path_is_sane(mg_str_n(buf, n))) return false; while (n > 1 && buf[n - 1] == '/') buf[--n] = 0; // Trim trailing slashes return true; diff --git a/test/unit_test.c b/test/unit_test.c index 6e730e6c..a16c3b25 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1519,6 +1519,7 @@ static void test_http_404(void) { ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, "hello\n") == 0); + ASSERT(fetch(&mgr, buf, url, "GET /a.txt%%00x HTTP/1.0\n\n") == 400); ASSERT(fetch(&mgr, buf, url, "GET /a/a.txt HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, "hello\n") == 0); ASSERT(fetch(&mgr, buf, url, "GET /unmapped HTTP/1.0\n\n") == 400);