1 |
From 154e565f6ef329c9ec97e6534c411ddde0b320c8 Mon Sep 17 00:00:00 2001 |
2 |
From: Sebastian Pipping <sebastian@pipping.org> |
3 |
Date: Sun, 20 Feb 2022 03:26:57 +0100 |
4 |
Subject: [PATCH] tests: Protect against nested element declaration model |
5 |
regressions |
6 |
|
7 |
--- |
8 |
expat/tests/runtests.c | 77 ++++++++++++++++++++++++++++++++++++++++++ |
9 |
1 file changed, 77 insertions(+) |
10 |
|
11 |
--- a/tests/runtests.c |
12 |
+++ b/tests/runtests.c |
13 |
@@ -1840,6 +1840,82 @@ START_TEST(test_dtd_default_handling) { |
14 |
} |
15 |
END_TEST |
16 |
|
17 |
+static void XMLCALL |
18 |
+element_decl_check_model(void *userData, const XML_Char *name, |
19 |
+ XML_Content *model) { |
20 |
+ UNUSED_P(userData); |
21 |
+ uint32_t errorFlags = 0; |
22 |
+ |
23 |
+ /* Expected model array structure is this: |
24 |
+ * [0] (type 6, quant 0) |
25 |
+ * [1] (type 5, quant 0) |
26 |
+ * [3] (type 4, quant 0, name "bar") |
27 |
+ * [4] (type 4, quant 0, name "foo") |
28 |
+ * [5] (type 4, quant 3, name "xyz") |
29 |
+ * [2] (type 4, quant 2, name "zebra") |
30 |
+ */ |
31 |
+ errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0)); |
32 |
+ errorFlags |= ((model != NULL) ? 0 : (1u << 1)); |
33 |
+ |
34 |
+ errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2)); |
35 |
+ errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3)); |
36 |
+ errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4)); |
37 |
+ errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5)); |
38 |
+ errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6)); |
39 |
+ |
40 |
+ errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7)); |
41 |
+ errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8)); |
42 |
+ errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9)); |
43 |
+ errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10)); |
44 |
+ errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11)); |
45 |
+ |
46 |
+ errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12)); |
47 |
+ errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13)); |
48 |
+ errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14)); |
49 |
+ errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15)); |
50 |
+ errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16)); |
51 |
+ |
52 |
+ errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17)); |
53 |
+ errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18)); |
54 |
+ errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19)); |
55 |
+ errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20)); |
56 |
+ errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21)); |
57 |
+ |
58 |
+ errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22)); |
59 |
+ errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23)); |
60 |
+ errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24)); |
61 |
+ errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25)); |
62 |
+ errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26)); |
63 |
+ |
64 |
+ errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27)); |
65 |
+ errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28)); |
66 |
+ errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29)); |
67 |
+ errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30)); |
68 |
+ errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31)); |
69 |
+ |
70 |
+ XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags); |
71 |
+ XML_FreeContentModel(g_parser, model); |
72 |
+} |
73 |
+ |
74 |
+START_TEST(test_dtd_elements_nesting) { |
75 |
+ // Payload inspired by a test in Perl's XML::Parser |
76 |
+ const char *text = "<!DOCTYPE foo [\n" |
77 |
+ "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n" |
78 |
+ "]>\n" |
79 |
+ "<foo/>"; |
80 |
+ |
81 |
+ XML_SetUserData(g_parser, (void *)(uintptr_t)-1); |
82 |
+ |
83 |
+ XML_SetElementDeclHandler(g_parser, element_decl_check_model); |
84 |
+ if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) |
85 |
+ == XML_STATUS_ERROR) |
86 |
+ xml_failure(g_parser); |
87 |
+ |
88 |
+ if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0) |
89 |
+ fail("Element declaration model regression detected"); |
90 |
+} |
91 |
+END_TEST |
92 |
+ |
93 |
/* Test handling of attribute declarations */ |
94 |
typedef struct AttTest { |
95 |
const char *definition; |
96 |
@@ -2663,6 +2739,82 @@ START_TEST(test_dtd_elements) { |
97 |
} |
98 |
END_TEST |
99 |
|
100 |
+static void XMLCALL |
101 |
+element_decl_check_model(void *userData, const XML_Char *name, |
102 |
+ XML_Content *model) { |
103 |
+ UNUSED_P(userData); |
104 |
+ uint32_t errorFlags = 0; |
105 |
+ |
106 |
+ /* Expected model array structure is this: |
107 |
+ * [0] (type 6, quant 0) |
108 |
+ * [1] (type 5, quant 0) |
109 |
+ * [3] (type 4, quant 0, name "bar") |
110 |
+ * [4] (type 4, quant 0, name "foo") |
111 |
+ * [5] (type 4, quant 3, name "xyz") |
112 |
+ * [2] (type 4, quant 2, name "zebra") |
113 |
+ */ |
114 |
+ errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0)); |
115 |
+ errorFlags |= ((model != NULL) ? 0 : (1u << 1)); |
116 |
+ |
117 |
+ errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2)); |
118 |
+ errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3)); |
119 |
+ errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4)); |
120 |
+ errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5)); |
121 |
+ errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6)); |
122 |
+ |
123 |
+ errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7)); |
124 |
+ errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8)); |
125 |
+ errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9)); |
126 |
+ errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10)); |
127 |
+ errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11)); |
128 |
+ |
129 |
+ errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12)); |
130 |
+ errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13)); |
131 |
+ errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14)); |
132 |
+ errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15)); |
133 |
+ errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16)); |
134 |
+ |
135 |
+ errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17)); |
136 |
+ errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18)); |
137 |
+ errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19)); |
138 |
+ errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20)); |
139 |
+ errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21)); |
140 |
+ |
141 |
+ errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22)); |
142 |
+ errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23)); |
143 |
+ errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24)); |
144 |
+ errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25)); |
145 |
+ errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26)); |
146 |
+ |
147 |
+ errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27)); |
148 |
+ errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28)); |
149 |
+ errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29)); |
150 |
+ errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30)); |
151 |
+ errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31)); |
152 |
+ |
153 |
+ XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags); |
154 |
+ XML_FreeContentModel(g_parser, model); |
155 |
+} |
156 |
+ |
157 |
+START_TEST(test_dtd_elements_nesting) { |
158 |
+ // Payload inspired by a test in Perl's XML::Parser |
159 |
+ const char *text = "<!DOCTYPE foo [\n" |
160 |
+ "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n" |
161 |
+ "]>\n" |
162 |
+ "<foo/>"; |
163 |
+ |
164 |
+ XML_SetUserData(g_parser, (void *)(uintptr_t)-1); |
165 |
+ |
166 |
+ XML_SetElementDeclHandler(g_parser, element_decl_check_model); |
167 |
+ if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE) |
168 |
+ == XML_STATUS_ERROR) |
169 |
+ xml_failure(g_parser); |
170 |
+ |
171 |
+ if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0) |
172 |
+ fail("Element declaration model regression detected"); |
173 |
+} |
174 |
+END_TEST |
175 |
+ |
176 |
/* Test foreign DTD handling */ |
177 |
START_TEST(test_set_foreign_dtd) { |
178 |
const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n"; |
179 |
@@ -11497,6 +11649,7 @@ make_suite(void) { |
180 |
tcase_add_test(tc_basic, test_attribute_enum_value); |
181 |
tcase_add_test(tc_basic, test_predefined_entity_redefinition); |
182 |
tcase_add_test__ifdef_xml_dtd(tc_basic, test_dtd_stop_processing); |
183 |
+ tcase_add_test(tc_basic, test_dtd_elements_nesting); |
184 |
tcase_add_test(tc_basic, test_public_notation_no_sysid); |
185 |
tcase_add_test(tc_basic, test_nested_groups); |
186 |
tcase_add_test(tc_basic, test_group_choice); |