civetweb: patch CVE-2025-9648

Details https://nvd.nist.gov/vuln/detail/CVE-2025-9648

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
(cherry picked from commit eb338ebb606f22363be5b4114e25a10b494b4f55)

Rebased patch on Kirkstone's civetweb.

Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
This commit is contained in:
Ankur Tyagi 2026-01-01 10:16:27 +01:00 committed by Gyorgy Sarvari
parent 15750d5584
commit 71adc2f371
2 changed files with 235 additions and 0 deletions

View File

@ -0,0 +1,234 @@
From 6f10111d24f9f7bdb637bba77c27700ecff56244 Mon Sep 17 00:00:00 2001
From: bel2125 <bel2125@gmail.com>
Date: Tue, 2 Sep 2025 14:08:41 +0200
Subject: [PATCH] Make parsing of URL encoded forms more robust
Reject requests that obviously violate the URL encoding.
Fixes #1348
CVE: CVE-2025-9648
Upstream-Status: Backport [https://github.com/civetweb/civetweb/commit/782e18903515f43bafbf2e668994e82bdfa51133]
(cherry picked from commit 782e18903515f43bafbf2e668994e82bdfa51133)
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
src/civetweb.c | 7 ++++++-
src/handle_form.inl | 46 +++++++++++++++++++++++++++++++++++++--------
2 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/src/civetweb.c b/src/civetweb.c
index 5452b36d..f843300c 100644
--- a/src/civetweb.c
+++ b/src/civetweb.c
@@ -7143,11 +7143,15 @@ mg_url_decode(const char *src,
i += 2;
} else if (is_form_url_encoded && (src[i] == '+')) {
dst[j] = ' ';
+ } else if ((unsigned char)src[i] <= ' ') {
+ return -1; /* invalid character */
} else {
dst[j] = src[i];
}
}
+#undef HEXTOI
+
dst[j] = '\0'; /* Null-terminate the destination */
return (i >= src_len) ? j : -1;
diff --git a/src/handle_form.inl b/src/handle_form.inl
index be477a05..0ebaf560 100644
--- a/src/handle_form.inl
+++ b/src/handle_form.inl
@@ -39,7 +39,7 @@ url_encoded_field_found(const struct mg_connection *conn,
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
- return MG_FORM_FIELD_STORAGE_SKIP;
+ return MG_FORM_FIELD_STORAGE_ABORT;
}
if (filename) {
@@ -53,7 +53,7 @@ url_encoded_field_found(const struct mg_connection *conn,
|| (filename_dec_len < 0)) {
/* Log error message and skip this field. */
mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
- return MG_FORM_FIELD_STORAGE_SKIP;
+ return MG_FORM_FIELD_STORAGE_ABORT;
}
remove_dot_segments(filename_dec);
@@ -93,6 +93,7 @@ url_encoded_field_get(
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
+ int key_dec_len;
char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->phys_ctx);
int value_dec_len, ret;
@@ -106,7 +107,8 @@ url_encoded_field_get(
return MG_FORM_FIELD_STORAGE_ABORT;
}
- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+ key_dec_len = mg_url_decode(
+ key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
value_dec_len =
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
@@ -111,6 +113,11 @@ url_encoded_field_get(
value_dec_len =
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
+ if ((key_dec_len < 0) || (value_dec_len < 0)) {
+ mg_free(value_dec);
+ return MG_FORM_FIELD_STORAGE_ABORT;
+ }
+
ret = fdh->field_get(key_dec,
value_dec,
(size_t)value_dec_len,
@@ -130,9 +137,13 @@ unencoded_field_get(const struct mg_connection *conn,
struct mg_form_data_handler *fdh)
{
char key_dec[1024];
+ int key_dec_len;
(void)conn;
- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+ key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+ if (key_dec_len < 0) {
+ return MG_FORM_FIELD_STORAGE_ABORT;
+ }
return fdh->field_get(key_dec, value, value_len, fdh->user_data);
}
@@ -182,6 +193,7 @@ mg_handle_form_request(struct mg_connection *conn,
int buf_fill = 0;
int r;
int field_count = 0;
+ int abort_read = 0;
struct mg_file fstore = STRUCT_FILE_INITIALIZER;
int64_t file_size = 0; /* init here, to a avoid a false positive
"uninitialized variable used" warning */
@@ -274,6 +286,7 @@ mg_handle_form_request(struct mg_connection *conn,
conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
if (r == MG_FORM_FIELD_HANDLE_NEXT) {
@@ -308,6 +321,7 @@ mg_handle_form_request(struct mg_connection *conn,
r = field_stored(conn, path, file_size, fdh);
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
@@ -346,6 +360,7 @@ mg_handle_form_request(struct mg_connection *conn,
if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
== MG_FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
+ abort_read = 1;
break;
}
@@ -374,7 +389,7 @@ mg_handle_form_request(struct mg_connection *conn,
* Here we use "POST", and read the data from the request body.
* The data read on the fly, so it is not required to buffer the
* entire request in memory before processing it. */
- for (;;) {
+ while (!abort_read) {
const char *val;
const char *next;
ptrdiff_t keylen, vallen;
@@ -428,6 +443,7 @@ mg_handle_form_request(struct mg_connection *conn,
if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
== MG_FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
+ abort_read = 1;
break;
}
@@ -458,6 +474,15 @@ mg_handle_form_request(struct mg_connection *conn,
vallen = (ptrdiff_t)strlen(val);
next = val + vallen;
end_of_key_value_pair_found = all_data_read;
+ if ((buf + buf_fill) > (val + vallen)) {
+ /* Avoid DoS attacks by having a zero byte in the middle of
+ * a request that is supposed to be URL encoded. Since this
+ * request is certainly invalid, according to the protocol
+ * specification, stop processing it. Fixes #1348 */
+ abort_read = 1;
+ break;
+ }
+
}
if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
@@ -479,6 +504,7 @@ mg_handle_form_request(struct mg_connection *conn,
get_block++;
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
if (r == MG_FORM_FIELD_HANDLE_NEXT) {
@@ -539,7 +565,6 @@ mg_handle_form_request(struct mg_connection *conn,
val = buf;
}
}
-
} while (!end_of_key_value_pair_found);
#if !defined(NO_FILESYSTEMS)
@@ -550,6 +575,7 @@ mg_handle_form_request(struct mg_connection *conn,
r = field_stored(conn, path, file_size, fdh);
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
} else {
@@ -563,7 +589,7 @@ mg_handle_form_request(struct mg_connection *conn,
}
#endif /* NO_FILESYSTEMS */
- if (all_data_read && (buf_fill == 0)) {
+ if ((all_data_read && (buf_fill == 0)) || abort_read) {
/* nothing more to process */
break;
}
@@ -919,6 +945,7 @@ mg_handle_form_request(struct mg_connection *conn,
get_block++;
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
if (r == MG_FORM_FIELD_HANDLE_NEXT) {
@@ -995,6 +1022,7 @@ mg_handle_form_request(struct mg_connection *conn,
fdh);
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
if (r == MG_FORM_FIELD_HANDLE_NEXT) {
@@ -1023,6 +1051,7 @@ mg_handle_form_request(struct mg_connection *conn,
r = field_stored(conn, path, file_size, fdh);
if (r == MG_FORM_FIELD_HANDLE_ABORT) {
/* Stop request handling */
+ abort_read = 1;
break;
}
} else {
@@ -1041,6 +1070,7 @@ mg_handle_form_request(struct mg_connection *conn,
if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
== MG_FORM_FIELD_STORAGE_ABORT) {
/* Stop parsing the request */
+ abort_read = 1;
break;
}

View File

@ -10,6 +10,7 @@ SRC_URI = "git://github.com/civetweb/civetweb.git;branch=master;protocol=https \
file://0001-Unittest-Link-librt-and-libm-using-l-option.patch \
file://0001-Sanitize-upload-filename-like-URL.patch \
file://0002-handle_form-example-Upload-to-temporary-directory-an.patch \
file://CVE-2025-9648.patch \
"
S = "${WORKDIR}/git"