Vulnerability: The parse_string_node function in bplist.c in libimobiledevice libplist 1.12 allows local users to cause a denial of service (memory allocation error) via a crafted plist file.
Vulnerability ID: CVE-2017-6436
CWE: CWE-119
Severity score: 2.9
Project: https://github.com/libimobiledevice/libplist
Published: 2017-03-15
File: src/bplist.c
Commit message: bplist: Fix data range check for string/data/dict/array nodes

Passing a size of 0xFFFFFFFFFFFFFFFF to parse_string_node() might result
in a memcpy with a size of -1, leading to undefined behavior.
This commit makes sure that the actual node data (which depends on the size)
is in the range start_of_object..start_of_object+size.

Credit to OSS-Fuzz
Commit author: Nikias Bassen

Vulnerable version (before fix):
 static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)         return parse_date_node(object, size);      case BPLIST_DATA:         if (*object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_DATA data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_data_node(object, size);      case BPLIST_STRING:         if (*object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_STRING data bytes point outside of valid range\n", __func__);             return NULL;         } static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)             PLIST_BIN_ERR("%s: Integer overflow when calculating BPLIST_UNICODE data size.\n", __func__);             return NULL;         }         if (*object + size*2 > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_UNICODE data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_unicode_node(object, size);      case BPLIST_SET:     case BPLIST_ARRAY:         if (*object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_ARRAY data bytes point outside of valid range\n", __func__);             return NULL;         } static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)         return parse_uid_node(object, size);      case BPLIST_DICT:         if (*object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_REAL data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_dict_node(bplist, object, size);

Fixed version (after fix):
 static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)         return parse_date_node(object, size);      case BPLIST_DATA:         if (*object + size < *object || *object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_DATA data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_data_node(object, size);      case BPLIST_STRING:         if (*object + size < *object || *object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_STRING data bytes point outside of valid range\n", __func__);             return NULL;         } static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)             PLIST_BIN_ERR("%s: Integer overflow when calculating BPLIST_UNICODE data size.\n", __func__);             return NULL;         }         if (*object + size*2 < *object || *object + size*2 > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_UNICODE data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_unicode_node(object, size);      case BPLIST_SET:     case BPLIST_ARRAY:         if (*object + size < *object || *object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_ARRAY data bytes point outside of valid range\n", __func__);             return NULL;         } static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)         return parse_uid_node(object, size);      case BPLIST_DICT:         if (*object + size < *object || *object + size > bplist->offset_table) {             PLIST_BIN_ERR("%s: BPLIST_DICT data bytes point outside of valid range\n", __func__);             return NULL;         }         return parse_dict_node(bplist, object, size);

Patch:
patch: @@ -654,14 +654,14 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
         return parse_date_node(object, size);
 
     case BPLIST_DATA:
-        if (*object + size > bplist->offset_table) {
+        if (*object + size < *object || *object + size > bplist->offset_table) {
             PLIST_BIN_ERR("%s: BPLIST_DATA data bytes point outside of valid range\n", __func__);
             return NULL;
         }
         return parse_data_node(object, size);
 
     case BPLIST_STRING:
-        if (*object + size > bplist->offset_table) {
+        if (*object + size < *object || *object + size > bplist->offset_table) {
             PLIST_BIN_ERR("%s: BPLIST_STRING data bytes point outside of valid range\n", __func__);
             return NULL;
         }
@@ -672,15 +672,15 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
             PLIST_BIN_ERR("%s: Integer overflow when calculating BPLIST_UNICODE data size.\n", __func__);
             return NULL;
         }
-        if (*object + size*2 > bplist->offset_table) {
+        if (*object + size*2 < *object || *object + size*2 > bplist->offset_table) {
             PLIST_BIN_ERR("%s: BPLIST_UNICODE data bytes point outside of valid range\n", __func__);
             return NULL;
         }
         return parse_unicode_node(object, size);
 
     case BPLIST_SET:
     case BPLIST_ARRAY:
-        if (*object + size > bplist->offset_table) {
+        if (*object + size < *object || *object + size > bplist->offset_table) {
             PLIST_BIN_ERR("%s: BPLIST_ARRAY data bytes point outside of valid range\n", __func__);
             return NULL;
         }
@@ -694,8 +694,8 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
         return parse_uid_node(object, size);
 
     case BPLIST_DICT:
-        if (*object + size > bplist->offset_table) {
-            PLIST_BIN_ERR("%s: BPLIST_REAL data bytes point outside of valid range\n", __func__);
+        if (*object + size < *object || *object + size > bplist->offset_table) {
+            PLIST_BIN_ERR("%s: BPLIST_DICT data bytes point outside of valid range\n", __func__);
             return NULL;
         }
         return parse_dict_node(bplist, object, size);
path: src/bplist.c