From: Tim Kientzle Date: Sun, 3 Apr 2016 18:03:22 +0000 (-0700) Subject: Issue 550: Fix out-of-bounds read in mtree. X-Git-Tag: v3.1.901a~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=64d56286ccc5d26d1eec1ef835e8e671f3ba6b84;p=thirdparty%2Flibarchive.git Issue 550: Fix out-of-bounds read in mtree. The mtree parser scanned from the end of the string to identify the filename when the filename is the last element of the line. If the filename was the entire line, the logic would scan back to before the start of the string. The revised logic scans from the beginning of the string and remembers the last separator position to locate the trailing filename. --- diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index 3abe19850..2b8cca7e5 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -856,7 +856,7 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq, *name, *end; - size_t len; + size_t name_len, len; int r; if ((entry = malloc(sizeof(*entry))) == NULL) { @@ -877,43 +877,48 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, *last_entry = entry; if (is_form_d) { - /* - * This form places the file name as last parameter. - */ - name = line + line_len -1; + /* Filename is last item on line. */ + /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { - if (*name != '\r' && *name != '\n' && - *name != '\t' && *name != ' ') + char last_character = line[line_len - 1]; + if (last_character == '\r' + || last_character == '\n' + || last_character == '\t' + || last_character == ' ') { + line_len--; + } else { break; - name--; - line_len--; + } } - len = 0; - while (line_len > 0) { - if (*name == '\r' || *name == '\n' || - *name == '\t' || *name == ' ') { - name++; - break; + /* Name starts after the last whitespace separator */ + name = line; + for (int i = 0; i < line_len; i++) { + if (line[i] == '\r' + || line[i] == '\n' + || line[i] == '\t' + || line[i] == ' ') { + name = line + i + 1; } - name--; - line_len--; - len++; } + name_len = line + line_len - name; end = name; } else { - len = strcspn(line, " \t\r\n"); + /* Filename is first item on line */ + name_len = strcspn(line, " \t\r\n"); name = line; - line += len; + line += name_len; end = line + line_len; } + /* name/name_len is the name within the line. */ + /* line..end brackets the entire line except the name */ - if ((entry->name = malloc(len + 1)) == NULL) { + if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } - memcpy(entry->name, name, len); - entry->name[len] = '\0'; + memcpy(entry->name, name, name_len); + entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); for (iter = *global; iter != NULL; iter = iter->next) {