-
Notifications
You must be signed in to change notification settings - Fork 607
Expand file tree
/
Copy pathCVE-2026-32778.patch
More file actions
145 lines (135 loc) · 5.15 KB
/
CVE-2026-32778.patch
File metadata and controls
145 lines (135 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
From 643882f03c3e3db0ec9e0d851322a9e087028622 Mon Sep 17 00:00:00 2001
From: laserbear <10689391+Laserbear@users.noreply.github.com>
Date: Sun, 8 Mar 2026 17:28:06 -0700
Subject: [PATCH 1/2] copy prefix name to pool before lookup
.. so that we cannot end up with a zombie PREFIX in the pool
that has NULL for a name.
Co-authored-by: Sebastian Pipping <sebastian@pipping.org>
---
lib/xmlparse.c | 43 +++++++++++++++++++++++++++++++++++--------
1 file changed, 35 insertions(+), 8 deletions(-)
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
index 88361a7..69027f6 100644
--- a/lib/xmlparse.c
+++ b/lib/xmlparse.c
@@ -590,6 +590,8 @@ static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
static XML_Bool FASTCALL poolGrow(STRING_POOL *pool);
static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool,
const XML_Char *s);
+static const XML_Char *FASTCALL poolCopyStringNoFinish(STRING_POOL *pool,
+ const XML_Char *s);
static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s,
int n);
static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool,
@@ -7431,16 +7433,24 @@ setContext(XML_Parser parser, const XML_Char *context) {
else {
if (! poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_FALSE;
- prefix
- = (PREFIX *)lookup(parser, &dtd->prefixes,
- poolStart(&parser->m_tempPool), sizeof(PREFIX));
- if (! prefix)
+ const XML_Char *const prefixName = poolCopyStringNoFinish(
+ &dtd->pool, poolStart(&parser->m_tempPool));
+ if (! prefixName) {
return XML_FALSE;
- if (prefix->name == poolStart(&parser->m_tempPool)) {
- prefix->name = poolCopyString(&dtd->pool, prefix->name);
- if (! prefix->name)
- return XML_FALSE;
}
+
+ prefix = (PREFIX *)lookup(parser, &dtd->prefixes, prefixName,
+ sizeof(PREFIX));
+
+ const bool prefixNameUsed = prefix && prefix->name == prefixName;
+ if (prefixNameUsed)
+ poolFinish(&dtd->pool);
+ else
+ poolDiscard(&dtd->pool);
+
+ if (! prefix)
+ return XML_FALSE;
+
poolDiscard(&parser->m_tempPool);
}
for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0');
@@ -8029,6 +8039,23 @@ poolCopyString(STRING_POOL *pool, const XML_Char *s) {
return s;
}
+// A version of `poolCopyString` that does not call `poolFinish`
+// and reverts any partial advancement upon failure.
+static const XML_Char *FASTCALL
+poolCopyStringNoFinish(STRING_POOL *pool, const XML_Char *s) {
+ const XML_Char *const original = s;
+ do {
+ if (! poolAppendChar(pool, *s)) {
+ // Revert any previously successful advancement
+ const ptrdiff_t advancedBy = s - original;
+ if (advancedBy > 0)
+ pool->ptr -= advancedBy;
+ return NULL;
+ }
+ } while (*s++);
+ return pool->start;
+}
+
static const XML_Char *
poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) {
if (! pool->ptr && ! poolGrow(pool)) {
--
2.45.4
From 8d541f0de1c513ab1030a2694041065d325f9aca Mon Sep 17 00:00:00 2001
From: laserbear <10689391+Laserbear@users.noreply.github.com>
Date: Sun, 8 Mar 2026 17:28:06 -0700
Subject: [PATCH 2/2] test that we do not end up with a zombie PREFIX in the
pool
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/libexpat/libexpat/pull/1163.patch
---
tests/nsalloc_tests.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c
index a8f5718..d284a58 100644
--- a/tests/nsalloc_tests.c
+++ b/tests/nsalloc_tests.c
@@ -1505,6 +1505,32 @@ START_TEST(test_nsalloc_prefixed_element) {
}
END_TEST
+/* Verify that retry after OOM in setContext() does not crash.
+ */
+START_TEST(test_nsalloc_setContext_zombie) {
+ const char *text = "<doc>Hello</doc>";
+ unsigned int i;
+ const unsigned int max_alloc_count = 30;
+
+ for (i = 0; i < max_alloc_count; i++) {
+ g_allocation_count = (int)i;
+ if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+ != XML_STATUS_ERROR)
+ break;
+ /* Retry on the same parser — must not crash */
+ g_allocation_count = ALLOC_ALWAYS_SUCCEED;
+ XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE);
+
+ nsalloc_teardown();
+ nsalloc_setup();
+ }
+ if (i == 0)
+ fail("Parsing worked despite failing allocations");
+ else if (i == max_alloc_count)
+ fail("Parsing failed even at maximum allocation count");
+}
+END_TEST
+
void
make_nsalloc_test_case(Suite *s) {
TCase *tc_nsalloc = tcase_create("namespace allocation tests");
@@ -1539,4 +1565,5 @@ make_nsalloc_test_case(Suite *s) {
tcase_add_test__if_xml_ge(tc_nsalloc, test_nsalloc_long_default_in_ext);
tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
+ tcase_add_test(tc_nsalloc, test_nsalloc_setContext_zombie);
}
--
2.45.4