From c8eb79f5257198dff7e65b30e8ec6f123d86254f Mon Sep 17 00:00:00 2001 From: lizhipeng Date: Tue, 30 Sep 2025 09:51:15 +0800 Subject: [PATCH] fix CVE-2025-9648 Signed-off-by: lizhipeng --- 0001-CVE-2025-9648.patch | 234 +++++++++++++++++++++++++++++++++++++++ civetweb.spec | 11 +- 2 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 0001-CVE-2025-9648.patch diff --git a/0001-CVE-2025-9648.patch b/0001-CVE-2025-9648.patch new file mode 100644 index 0000000..40580ef --- /dev/null +++ b/0001-CVE-2025-9648.patch @@ -0,0 +1,234 @@ +diff --git a/src/civetweb.c b/src/civetweb.c +index a7191fc..2b96e33 100644 +--- a/src/civetweb.c ++++ b/src/civetweb.c +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2013-2020 the Civetweb developers ++/* Copyright (c) 2013-2025 the Civetweb developers + * Copyright (c) 2004-2013 Sergey Lyubka + * + * Permission is hereby granted, free of charge, to any person obtaining a copy +@@ -7131,6 +7131,7 @@ mg_url_decode(const char *src, + int is_form_url_encoded) + { + int i, j, a, b; ++ + #define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W')) + + for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) { +@@ -7143,11 +7144,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 9853faf..c1c9c5c 100644 +--- a/src/handle_form.inl ++++ b/src/handle_form.inl +@@ -1,4 +1,4 @@ +-/* Copyright (c) 2016-2020 the Civetweb developers ++/* Copyright (c) 2016-2025 the Civetweb developers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal +@@ -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; + } + } else { + filename_dec[0] = 0; +@@ -91,6 +91,7 @@ url_encoded_field_get(const struct mg_connection *conn, + 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; +@@ -104,11 +105,17 @@ url_encoded_field_get(const struct mg_connection *conn, + 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); + ++ 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, +@@ -128,9 +135,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); + } +@@ -180,6 +191,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 */ +@@ -272,6 +284,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) { +@@ -306,6 +319,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; + } + +@@ -344,6 +358,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; + } + +@@ -372,7 +387,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; +@@ -426,6 +441,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; + } + +@@ -456,6 +472,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) { +@@ -477,6 +502,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) { +@@ -537,7 +563,6 @@ mg_handle_form_request(struct mg_connection *conn, + val = buf; + } + } +- + } while (!end_of_key_value_pair_found); + + #if !defined(NO_FILESYSTEMS) +@@ -548,6 +573,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 { +@@ -561,7 +587,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; + } +@@ -917,6 +943,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) { +@@ -993,6 +1020,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) { +@@ -1021,6 +1049,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 { +@@ -1039,6 +1068,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; + } + diff --git a/civetweb.spec b/civetweb.spec index 3288027..899736a 100644 --- a/civetweb.spec +++ b/civetweb.spec @@ -1,10 +1,14 @@ Name: civetweb Summary: Embedded C/C++ web server Version: 1.12 -Release: 1 +Release: 2 License: MIT URL: https://github.com/%{name}/%{name} Source: https://github.com/%{name}/%{name}/archive/refs/tags/v%{version}.tar.gz + +#backport +Patch1: 0001-CVE-2025-9648.patch + BuildRequires: cmake make gcc-c++ %description @@ -24,7 +28,7 @@ Requires: %{name}%{?_isa} = %{version}-%{release} Civetweb shared libs and associated header files %prep -%setup -q -n %{name}-%{version} +%autosetup -p1 -n %{name}-%{version} %build rm -rf build @@ -62,5 +66,8 @@ DESTDIR="%{buildroot}" cmake --install . --prefix=/usr %{_libdir}/cmake/civetweb/* %changelog +* Tue Sep 30 2025 lizhipeng - 1.12-2 +- fix CVE-2025-9648 + * Mon Apr 7 2025 fuanan - 1.12-1 - package init -- Gitee