summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2017-03-29 02:51:00 +0200
committerGravatar Nikias Bassen2017-03-29 02:51:00 +0200
commit012e4a8d7c99baf326cbcf06eabe899e2cdf66f7 (patch)
tree89224d1c2183f14b6e073fb3d9e34ad4e06e8798
parent1406766a0c75f6a861afb73f1606036f2da79bd5 (diff)
downloadlibplist-012e4a8d7c99baf326cbcf06eabe899e2cdf66f7.tar.gz
libplist-012e4a8d7c99baf326cbcf06eabe899e2cdf66f7.tar.bz2
xplist: Make XML parsing non-recursive to prevent stack overflow on deep-structured plists
Credit to OSS-Fuzz
-rw-r--r--src/xplist.c182
1 files changed, 103 insertions, 79 deletions
diff --git a/src/xplist.c b/src/xplist.c
index 7f20ef2..022f1cd 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -2,7 +2,7 @@
2 * xplist.c 2 * xplist.c
3 * XML plist implementation 3 * XML plist implementation
4 * 4 *
5 * Copyright (c) 2010-2016 Nikias Bassen All Rights Reserved. 5 * Copyright (c) 2010-2017 Nikias Bassen All Rights Reserved.
6 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. 6 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
7 * Copyright (c) 2008 Jonathan Beck All Rights Reserved. 7 * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
8 * 8 *
@@ -804,12 +804,21 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t
804 return str; 804 return str;
805} 805}
806 806
807static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth) 807static void node_from_xml(parse_ctx ctx, plist_t *plist)
808{ 808{
809 char *tag = NULL; 809 char *tag = NULL;
810 char *keyname = NULL; 810 char *keyname = NULL;
811 plist_t subnode = NULL; 811 plist_t subnode = NULL;
812 const char *p = NULL; 812 const char *p = NULL;
813 plist_t parent = NULL;
814 int has_content = 0;
815
816 struct node_path_item {
817 const char *type;
818 void *prev;
819 };
820 struct node_path_item* node_path = NULL;
821
813 while (ctx->pos < ctx->end && !ctx->err) { 822 while (ctx->pos < ctx->end && !ctx->err) {
814 parse_skip_ws(ctx); 823 parse_skip_ws(ctx);
815 if (ctx->pos >= ctx->end) { 824 if (ctx->pos >= ctx->end) {
@@ -929,28 +938,58 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth)
929 if (!strcmp(tag, "plist")) { 938 if (!strcmp(tag, "plist")) {
930 free(tag); 939 free(tag);
931 tag = NULL; 940 tag = NULL;
941 has_content = 0;
942
943 if (!node_path && *plist) {
944 /* we don't allow another top-level <plist> */
945 break;
946 }
932 if (is_empty) { 947 if (is_empty) {
933 PLIST_XML_ERR("Empty plist tag\n"); 948 PLIST_XML_ERR("Empty plist tag\n");
934 break; 949 ctx->err++;
950 goto err_out;
935 } 951 }
936 if (!*plist) { 952
937 /* only process first plist node found */ 953 struct node_path_item *path_item = malloc(sizeof(struct node_path_item));
938 node_from_xml(ctx, plist, depth+1); 954 if (!path_item) {
955 PLIST_XML_ERR("out of memory when allocating node path item\n");
956 ctx->err++;
957 goto err_out;
939 } 958 }
959 path_item->type = "plist";
960 path_item->prev = node_path;
961 node_path = path_item;
962
940 continue; 963 continue;
941 } else if (depth == 1 && !strcmp(tag, "/plist")) { 964 } else if (!strcmp(tag, "/plist")) {
942 if (!*plist) { 965 if (!has_content) {
943 PLIST_XML_ERR("Empty plist tag\n"); 966 PLIST_XML_ERR("encountered empty plist tag\n");
967 ctx->err++;
968 goto err_out;
944 } 969 }
945 goto err_out; 970 if (!node_path) {
946 } else if (depth == 1 && *plist) { 971 PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n");
947 PLIST_XML_ERR("Unexpected tag <%s> found while </plist> is expected\n", tag); 972 ctx->err++;
948 ctx->err++; 973 goto err_out;
949 goto err_out; 974 }
975 if (strcmp(node_path->type, tag+1) != 0) {
976 PLIST_XML_ERR("mismatching closing tag <%s> found for opening tag <%s>\n", tag, node_path->type);
977 ctx->err++;
978 goto err_out;
979 }
980 struct node_path_item *path_item = node_path;
981 node_path = node_path->prev;
982 free(path_item);
983
984 free(tag);
985 tag = NULL;
986
987 continue;
950 } 988 }
951 989
952 plist_data_t data = plist_new_plist_data(); 990 plist_data_t data = plist_new_plist_data();
953 subnode = plist_new_node(data); 991 subnode = plist_new_node(data);
992 has_content = 1;
954 993
955 if (!strcmp(tag, XPLIST_DICT)) { 994 if (!strcmp(tag, XPLIST_DICT)) {
956 data->type = PLIST_DICT; 995 data->type = PLIST_DICT;
@@ -1068,7 +1107,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth)
1068 ctx->err++; 1107 ctx->err++;
1069 goto err_out; 1108 goto err_out;
1070 } 1109 }
1071 if (!strcmp(tag, "key") && !keyname && *plist && (plist_get_node_type(*plist) == PLIST_DICT)) { 1110 if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) {
1072 keyname = str; 1111 keyname = str;
1073 free(tag); 1112 free(tag);
1074 tag = NULL; 1113 tag = NULL;
@@ -1167,100 +1206,78 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth)
1167 goto err_out; 1206 goto err_out;
1168 } 1207 }
1169 if (subnode && !closing_tag) { 1208 if (subnode && !closing_tag) {
1170 /* parse sub nodes for structured types */
1171 if (data->type == PLIST_DICT || data->type == PLIST_ARRAY) {
1172 if (!is_empty) {
1173 /* only if not empty */
1174 node_from_xml(ctx, &subnode, depth+1);
1175 if (ctx->err) {
1176 /* make sure to bail out if parsing failed */
1177 goto err_out;
1178 }
1179 if ((data->type == PLIST_DICT) && (plist_dict_get_size(subnode) == 1)) {
1180 /* convert XML CF$UID dictionaries to PLIST_UID nodes */
1181 plist_t uid = plist_dict_get_item(subnode, "CF$UID");
1182 if (uid) {
1183 uint64_t val = 0;
1184 if (plist_get_node_type(uid) != PLIST_UINT) {
1185 ctx->err++;
1186 PLIST_XML_ERR("Invalid node type for CF$UID dict entry (must be PLIST_UINT)\n");
1187 goto err_out;
1188 }
1189 plist_get_uint_val(uid, &val);
1190 plist_dict_remove_item(subnode, "CF$UID");
1191 plist_data_t nodedata = plist_get_data((node_t*)subnode);
1192 free(nodedata->buff);
1193 nodedata->type = PLIST_UID;
1194 nodedata->length = sizeof(uint64_t);
1195 nodedata->intval = val;
1196 }
1197 }
1198 }
1199 }
1200 if (!*plist) { 1209 if (!*plist) {
1201 /* no parent? make this node the new parent node */ 1210 /* first node, make this node the parent node */
1202 *plist = subnode; 1211 *plist = subnode;
1203 subnode = NULL; 1212 if (data->type != PLIST_DICT && data->type != PLIST_ARRAY) {
1204 } else { 1213 /* if the first node is not a structered node, we're done */
1205 switch (plist_get_node_type(*plist)) { 1214 subnode = NULL;
1215 goto err_out;
1216 }
1217 parent = subnode;
1218 } else if (parent) {
1219 switch (plist_get_node_type(parent)) {
1206 case PLIST_DICT: 1220 case PLIST_DICT:
1207 if (!keyname) { 1221 if (!keyname) {
1208 PLIST_XML_ERR("missing key name while adding dict item\n"); 1222 PLIST_XML_ERR("missing key name while adding dict item\n");
1209 ctx->err++; 1223 ctx->err++;
1210 break; 1224 break;
1211 } 1225 }
1212 plist_dict_set_item(*plist, keyname, subnode); 1226 plist_dict_set_item(parent, keyname, subnode);
1213 subnode = NULL;
1214 break; 1227 break;
1215 case PLIST_ARRAY: 1228 case PLIST_ARRAY:
1216 plist_array_append_item(*plist, subnode); 1229 plist_array_append_item(parent, subnode);
1217 subnode = NULL;
1218 break; 1230 break;
1219 default: 1231 default:
1220 /* should not happen */ 1232 /* should not happen */
1221 PLIST_XML_ERR("while parsing XML plist: parent is not a structered node.\n"); 1233 PLIST_XML_ERR("parent is not a structered node\n");
1222 ctx->err++; 1234 ctx->err++;
1223 break; 1235 break;
1224 } 1236 }
1225 } 1237 }
1226 } else if (closing_tag && *plist) { 1238 if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) {
1227 switch (plist_get_node_type(*plist)) { 1239 struct node_path_item *path_item = malloc(sizeof(struct node_path_item));
1228 case PLIST_DICT: 1240 if (!path_item) {
1229 if (keyname) { 1241 PLIST_XML_ERR("out of memory when allocating node path item\n");
1230 PLIST_XML_ERR("missing value node in dict\n");
1231 ctx->err++;
1232 } else if (strcmp(tag+1, XPLIST_DICT) != 0) {
1233 PLIST_XML_ERR("closing tag mismatch, expected: </%s> found: <%s>\n", XPLIST_DICT, tag);
1234 ctx->err++;
1235 }
1236 break;
1237 case PLIST_ARRAY:
1238 if (strcmp(tag+1, XPLIST_ARRAY) != 0) {
1239 PLIST_XML_ERR("closing tag mismatch, expected: </%s> found: <%s>\n", XPLIST_ARRAY, tag);
1240 ctx->err++; 1242 ctx->err++;
1243 goto err_out;
1241 } 1244 }
1242 break; 1245 path_item->type = (data->type == PLIST_DICT) ? XPLIST_DICT : XPLIST_ARRAY;
1243 default: 1246 path_item->prev = node_path;
1244 /* should not happen */ 1247 node_path = path_item;
1245 PLIST_XML_ERR("expected structered node but got type %d\n", plist_get_node_type(*plist)); 1248
1249 parent = subnode;
1250 }
1251 subnode = NULL;
1252 } else if (closing_tag) {
1253 if (!node_path) {
1254 PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n");
1246 ctx->err++; 1255 ctx->err++;
1247 break; 1256 goto err_out;
1248 } 1257 }
1258 if (strcmp(node_path->type, tag+1) != 0) {
1259 PLIST_XML_ERR("unexpected %s found (for opening %s)\n", tag, node_path->type);
1260 ctx->err++;
1261 goto err_out;
1262 }
1263 struct node_path_item *path_item = node_path;
1264 node_path = node_path->prev;
1265 free(path_item);
1266
1267 parent = ((node_t*)parent)->parent;
1249 } 1268 }
1269
1250 free(tag); 1270 free(tag);
1251 tag = NULL; 1271 tag = NULL;
1252 free(keyname); 1272 free(keyname);
1253 keyname = NULL; 1273 keyname = NULL;
1254 plist_free(subnode); 1274 plist_free(subnode);
1255 subnode = NULL; 1275 subnode = NULL;
1256 if (closing_tag) {
1257 break;
1258 }
1259 } 1276 }
1260 } 1277 }
1261 1278
1262 if (depth == 1) { 1279 if (node_path) {
1263 PLIST_XML_ERR("EOF while </plist> tag is expected\n"); 1280 PLIST_XML_ERR("EOF encountered while </%s> was expected\n", node_path->type);
1264 ctx->err++; 1281 ctx->err++;
1265 } 1282 }
1266 1283
@@ -1269,6 +1286,13 @@ err_out:
1269 free(keyname); 1286 free(keyname);
1270 plist_free(subnode); 1287 plist_free(subnode);
1271 1288
1289 /* clean up node_path if required */
1290 while (node_path) {
1291 struct node_path_item *path_item = node_path;
1292 node_path = path_item->prev;
1293 free(path_item);
1294 }
1295
1272 if (ctx->err) { 1296 if (ctx->err) {
1273 plist_free(*plist); 1297 plist_free(*plist);
1274 *plist = NULL; 1298 *plist = NULL;
@@ -1284,5 +1308,5 @@ PLIST_API void plist_from_xml(const char *plist_xml, uint32_t length, plist_t *
1284 1308
1285 struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 }; 1309 struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 };
1286 1310
1287 node_from_xml(&ctx, plist, 0); 1311 node_from_xml(&ctx, plist);
1288} 1312}