Skip to content

Commit 5434653

Browse files
azurelinux-securityBinduSri-6522866Kanishk-BansalPawelWMS
authored
[AutoPR- Security] Patch ceph for CVE-2025-9648 [HIGH] (microsoft#14778)
Co-authored-by: BinduSri-6522866 <[email protected]> Co-authored-by: Kanishk Bansal <[email protected]> Co-authored-by: Pawel Winogrodzki <[email protected]>
1 parent 02cd34e commit 5434653

File tree

2 files changed

+382
-3
lines changed

2 files changed

+382
-3
lines changed

SPECS/ceph/CVE-2025-9648.patch

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
From 72fb60683f2f8f2cd253213bb0cc275bca323c64 Mon Sep 17 00:00:00 2001
2+
From: AllSpark <[email protected]>
3+
Date: Fri, 3 Oct 2025 06:15:09 +0000
4+
Subject: [PATCH] Make parsing of URL encoded forms more robust: reject invalid
5+
control characters in mg_url_decode; abort form processing on decode errors;
6+
add abort_read control in URL-encoded POST handling; update copyrights
7+
8+
Signed-off-by: Azure Linux Security Servicing Account <[email protected]>
9+
10+
Modified to apply to Azure Linux
11+
Modified by: BinduSri-6522866 <[email protected]>
12+
Upstream Patch Reference: https://github.com/civetweb/civetweb/commit/782e18903515f43bafbf2e668994e82bdfa51133.patch
13+
---
14+
src/civetweb/include/civetweb.h | 2 +
15+
src/civetweb/src/civetweb.c | 7 +-
16+
src/civetweb/src/handle_form.inl | 127 +++++++++++++++++++++++++++----
17+
3 files changed, 119 insertions(+), 17 deletions(-)
18+
19+
diff --git a/src/civetweb/include/civetweb.h b/src/civetweb/include/civetweb.h
20+
index 6ddde4f2f..e0a2382e8 100644
21+
--- a/src/civetweb/include/civetweb.h
22+
+++ b/src/civetweb/include/civetweb.h
23+
@@ -1109,6 +1109,8 @@ enum {
24+
MG_FORM_FIELD_STORAGE_GET = 0x1,
25+
/* Store the field value into a file. */
26+
MG_FORM_FIELD_STORAGE_STORE = 0x2,
27+
+ /* Handle the next field */
28+
+ MG_FORM_FIELD_STORAGE_NEXT = 0x8,
29+
/* Stop parsing this request. Skip the remaining fields. */
30+
MG_FORM_FIELD_STORAGE_ABORT = 0x10
31+
};
32+
diff --git a/src/civetweb/src/civetweb.c b/src/civetweb/src/civetweb.c
33+
index 76f097ac1..508a910ac 100644
34+
--- a/src/civetweb/src/civetweb.c
35+
+++ b/src/civetweb/src/civetweb.c
36+
@@ -1,4 +1,4 @@
37+
-/* Copyright (c) 2013-2017 the Civetweb developers
38+
+/* Copyright (c) 2013-2025 the Civetweb developers
39+
* Copyright (c) 2004-2013 Sergey Lyubka
40+
*
41+
* Permission is hereby granted, free of charge, to any person obtaining a copy
42+
@@ -6365,6 +6365,7 @@ mg_url_decode(const char *src,
43+
int is_form_url_encoded)
44+
{
45+
int i, j, a, b;
46+
+
47+
#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
48+
49+
for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
50+
@@ -6377,11 +6378,15 @@ mg_url_decode(const char *src,
51+
i += 2;
52+
} else if (is_form_url_encoded && (src[i] == '+')) {
53+
dst[j] = ' ';
54+
+ } else if ((unsigned char)src[i] <= ' ') {
55+
+ return -1; /* invalid character */
56+
} else {
57+
dst[j] = src[i];
58+
}
59+
}
60+
61+
+#undef HEXTOI
62+
+
63+
dst[j] = '\0'; /* Null-terminate the destination */
64+
65+
return (i >= src_len) ? j : -1;
66+
diff --git a/src/civetweb/src/handle_form.inl b/src/civetweb/src/handle_form.inl
67+
index 2a213ade5..a805ccafa 100644
68+
--- a/src/civetweb/src/handle_form.inl
69+
+++ b/src/civetweb/src/handle_form.inl
70+
@@ -1,4 +1,4 @@
71+
-/* Copyright (c) 2016-2017 the Civetweb developers
72+
+/* Copyright (c) 2016-2025 the Civetweb developers
73+
*
74+
* Permission is hereby granted, free of charge, to any person obtaining a copy
75+
* of this software and associated documentation files (the "Software"), to deal
76+
@@ -40,7 +40,7 @@ url_encoded_field_found(const struct mg_connection *conn,
77+
mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
78+
79+
if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
80+
- return FORM_FIELD_STORAGE_SKIP;
81+
+ return FORM_FIELD_STORAGE_ABORT;
82+
}
83+
84+
if (filename) {
85+
@@ -54,7 +54,7 @@ url_encoded_field_found(const struct mg_connection *conn,
86+
|| (filename_dec_len < 0)) {
87+
/* Log error message and skip this field. */
88+
mg_cry(conn, "%s: Cannot decode filename", __func__);
89+
- return FORM_FIELD_STORAGE_SKIP;
90+
+ return FORM_FIELD_STORAGE_ABORT;
91+
}
92+
} else {
93+
filename_dec[0] = 0;
94+
@@ -89,6 +89,7 @@ url_encoded_field_get(const struct mg_connection *conn,
95+
struct mg_form_data_handler *fdh)
96+
{
97+
char key_dec[1024];
98+
+ int key_dec_len;
99+
100+
char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->ctx);
101+
int value_dec_len, ret;
102+
@@ -102,11 +103,18 @@ url_encoded_field_get(const struct mg_connection *conn,
103+
return FORM_FIELD_STORAGE_ABORT;
104+
}
105+
106+
- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
107+
+ key_dec_len = mg_url_decode(
108+
+ key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
109+
110+
value_dec_len =
111+
mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
112+
113+
+ if ((key_dec_len < 0) || (value_dec_len < 0)) {
114+
+ mg_free(value_dec);
115+
+ return MG_FORM_FIELD_STORAGE_ABORT;
116+
+ }
117+
+
118+
+
119+
ret = fdh->field_get(key_dec,
120+
value_dec,
121+
(size_t)value_dec_len,
122+
@@ -127,9 +135,13 @@ unencoded_field_get(const struct mg_connection *conn,
123+
struct mg_form_data_handler *fdh)
124+
{
125+
char key_dec[1024];
126+
+ int key_dec_len;
127+
(void)conn;
128+
129+
- mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
130+
+ key_dec_len = mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
131+
+ if (key_dec_len < 0) {
132+
+ return MG_FORM_FIELD_STORAGE_ABORT;
133+
+ }
134+
135+
return fdh->field_get(key_dec, value, value_len, fdh->user_data);
136+
}
137+
@@ -182,6 +194,7 @@ mg_handle_form_request(struct mg_connection *conn,
138+
int buf_fill = 0;
139+
int r;
140+
int field_count = 0;
141+
+ int abort_read = 0;
142+
struct mg_file fstore = STRUCT_FILE_INITIALIZER;
143+
int64_t file_size = 0; /* init here, to a avoid a false positive
144+
"uninitialized variable used" warning */
145+
@@ -264,9 +277,27 @@ mg_handle_form_request(struct mg_connection *conn,
146+
147+
if (field_storage == FORM_FIELD_STORAGE_GET) {
148+
/* Call callback */
149+
- url_encoded_field_get(
150+
+ r = url_encoded_field_get(
151+
conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
152+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
153+
+ /* Stop request handling */
154+
+ abort_read = 1;
155+
+ break;
156+
+ }
157+
+ if (r == MG_FORM_FIELD_STORAGE_NEXT) {
158+
+ /* Skip to next field */
159+
+ field_storage = MG_FORM_FIELD_STORAGE_SKIP;
160+
+ }
161+
+
162+
+ }
163+
+
164+
+ if (next) {
165+
+ next++;
166+
+ } else {
167+
+ /* vallen may have been modified by url_encoded_field_get */
168+
+ next = val + vallen;
169+
}
170+
+
171+
if (field_storage == FORM_FIELD_STORAGE_STORE) {
172+
/* Store the content to a file */
173+
if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
174+
@@ -290,7 +321,12 @@ mg_handle_form_request(struct mg_connection *conn,
175+
r = mg_fclose(&fstore.access);
176+
if (r == 0) {
177+
/* stored successfully */
178+
- field_stored(conn, path, file_size, fdh);
179+
+ r = field_stored(conn, path, file_size, fdh);
180+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
181+
+ /* Stop request handling */
182+
+ abort_read = 1;
183+
+ break;
184+
+ }
185+
} else {
186+
mg_cry(conn,
187+
"%s: Error saving file %s",
188+
@@ -322,6 +358,7 @@ mg_handle_form_request(struct mg_connection *conn,
189+
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
190+
== FORM_FIELD_STORAGE_ABORT) {
191+
/* Stop parsing the request */
192+
+ abort_read = 1;
193+
break;
194+
}
195+
196+
@@ -346,7 +383,7 @@ mg_handle_form_request(struct mg_connection *conn,
197+
* Here we use "POST", and read the data from the request body.
198+
* The data read on the fly, so it is not required to buffer the
199+
* entire request in memory before processing it. */
200+
- for (;;) {
201+
+ while (!abort_read) {
202+
const char *val;
203+
const char *next;
204+
ptrdiff_t keylen, vallen;
205+
@@ -400,6 +437,7 @@ mg_handle_form_request(struct mg_connection *conn,
206+
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
207+
== FORM_FIELD_STORAGE_ABORT) {
208+
/* Stop parsing the request */
209+
+ abort_read = 1;
210+
break;
211+
}
212+
213+
@@ -419,8 +457,15 @@ mg_handle_form_request(struct mg_connection *conn,
214+
next = strchr(val, '&');
215+
if (next) {
216+
vallen = next - val;
217+
- next++;
218+
- end_of_key_value_pair_found = 1;
219+
+ end_of_key_value_pair_found = all_data_read;
220+
+ if ((buf + buf_fill) > (val + vallen)) {
221+
+ /* Avoid DoS attacks by having a zero byte in the middle of
222+
+ * a request that is supposed to be URL encoded. Since this
223+
+ * request is certainly invalid, according to the protocol
224+
+ * specification, stop processing it. Fixes #1348 */
225+
+ abort_read = 1;
226+
+ break;
227+
+ }
228+
} else {
229+
vallen = (ptrdiff_t)strlen(val);
230+
next = val + vallen;
231+
@@ -436,7 +481,7 @@ mg_handle_form_request(struct mg_connection *conn,
232+
#endif
233+
234+
/* Call callback */
235+
- url_encoded_field_get(conn,
236+
+ r = url_encoded_field_get(conn,
237+
((get_block > 0) ? NULL : buf),
238+
((get_block > 0) ? 0
239+
: (size_t)keylen),
240+
@@ -444,7 +489,23 @@ mg_handle_form_request(struct mg_connection *conn,
241+
(size_t)vallen,
242+
fdh);
243+
get_block++;
244+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
245+
+ /* Stop request handling */
246+
+ abort_read = 1;
247+
+ break;
248+
+ }
249+
+ if (r == MG_FORM_FIELD_STORAGE_NEXT) {
250+
+ /* Skip to next field */
251+
+ field_storage = MG_FORM_FIELD_STORAGE_SKIP;
252+
+ }
253+
+ }
254+
+ if (next) {
255+
+ next++;
256+
+ } else {
257+
+ /* vallen may have been modified by url_encoded_field_get */
258+
+ next = val + vallen;
259+
}
260+
+
261+
if (fstore.access.fp) {
262+
size_t n = (size_t)
263+
fwrite(val, 1, (size_t)vallen, fstore.access.fp);
264+
@@ -489,14 +550,18 @@ mg_handle_form_request(struct mg_connection *conn,
265+
val = buf;
266+
}
267+
}
268+
-
269+
} while (!end_of_key_value_pair_found);
270+
271+
if (fstore.access.fp) {
272+
r = mg_fclose(&fstore.access);
273+
if (r == 0) {
274+
/* stored successfully */
275+
- field_stored(conn, path, file_size, fdh);
276+
+ r = field_stored(conn, path, file_size, fdh);
277+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
278+
+ /* Stop request handling */
279+
+ abort_read = 1;
280+
+ break;
281+
+ }
282+
} else {
283+
mg_cry(conn, "%s: Error saving file %s", __func__, path);
284+
remove_bad_file(conn, path);
285+
@@ -504,6 +569,11 @@ mg_handle_form_request(struct mg_connection *conn,
286+
fstore.access.fp = NULL;
287+
}
288+
289+
+ if ((all_data_read && (buf_fill == 0)) || abort_read) {
290+
+ /* nothing more to process */
291+
+ break;
292+
+ }
293+
+
294+
/* Proceed to next entry */
295+
used = next - buf;
296+
memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
297+
@@ -829,7 +899,7 @@ mg_handle_form_request(struct mg_connection *conn,
298+
towrite -= bl + 4;
299+
300+
if (field_storage == FORM_FIELD_STORAGE_GET) {
301+
- unencoded_field_get(conn,
302+
+ r = unencoded_field_get(conn,
303+
((get_block > 0) ? NULL : nbeg),
304+
((get_block > 0)
305+
? 0
306+
@@ -838,6 +908,15 @@ mg_handle_form_request(struct mg_connection *conn,
307+
towrite,
308+
fdh);
309+
get_block++;
310+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
311+
+ /* Stop request handling */
312+
+ abort_read = 1;
313+
+ break;
314+
+ }
315+
+ if (r == MG_FORM_FIELD_STORAGE_NEXT) {
316+
+ /* Skip to next field */
317+
+ field_storage = MG_FORM_FIELD_STORAGE_SKIP;
318+
+ }
319+
}
320+
321+
if (field_storage == FORM_FIELD_STORAGE_STORE) {
322+
@@ -880,19 +959,29 @@ mg_handle_form_request(struct mg_connection *conn,
323+
324+
/* Find boundary */
325+
next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
326+
+
327+
}
328+
329+
towrite = (size_t)(next - hend);
330+
331+
if (field_storage == FORM_FIELD_STORAGE_GET) {
332+
/* Call callback */
333+
- unencoded_field_get(conn,
334+
+ r = unencoded_field_get(conn,
335+
((get_block > 0) ? NULL : nbeg),
336+
((get_block > 0) ? 0
337+
: (size_t)(nend - nbeg)),
338+
hend,
339+
towrite,
340+
fdh);
341+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
342+
+ /* Stop request handling */
343+
+ abort_read = 1;
344+
+ break;
345+
+ }
346+
+ if (r == MG_FORM_FIELD_STORAGE_NEXT) {
347+
+ /* Skip to next field */
348+
+ field_storage = MG_FORM_FIELD_STORAGE_SKIP;
349+
+ }
350+
}
351+
352+
if (field_storage == FORM_FIELD_STORAGE_STORE) {
353+
@@ -911,7 +1000,12 @@ mg_handle_form_request(struct mg_connection *conn,
354+
r = mg_fclose(&fstore.access);
355+
if (r == 0) {
356+
/* stored successfully */
357+
- field_stored(conn, path, file_size, fdh);
358+
+ r = field_stored(conn, path, file_size, fdh);
359+
+ if (r == MG_FORM_FIELD_STORAGE_ABORT) {
360+
+ /* Stop request handling */
361+
+ abort_read = 1;
362+
+ break;
363+
+ }
364+
} else {
365+
mg_cry(conn,
366+
"%s: Error saving file %s",
367+
@@ -927,6 +1021,7 @@ mg_handle_form_request(struct mg_connection *conn,
368+
if ((field_storage & FORM_FIELD_STORAGE_ABORT)
369+
== FORM_FIELD_STORAGE_ABORT) {
370+
/* Stop parsing the request */
371+
+ abort_read = 1;
372+
break;
373+
}
374+
375+
--
376+
2.45.4
377+

0 commit comments

Comments
 (0)