diff options
-rw-r--r-- | src/xdgmimecache.c | 71 |
1 files changed, 70 insertions, 1 deletions
diff --git a/src/xdgmimecache.c b/src/xdgmimecache.c index e700089..c8c717a 100644 --- a/src/xdgmimecache.c +++ b/src/xdgmimecache.c @@ -76,6 +76,13 @@ struct _XdgMimeCache #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset)))) #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset)))) +// Validates that it is safe to call GET_UINT32() at +// cache->buffer[offset + (n * record_size)]. Ensures that offset is aligned to +// a 4-byte boundary, and that offset+(n*record_size) does not overflow. +// `record_size` values are known constants and never 0. +#define OUT_OF_BOUNDS(offset,n,record_size,max) \ + ((offset & 0x3) || offset > max || n > (max - offset) / record_size) + XdgMimeCache * _xdg_mime_cache_ref (XdgMimeCache *cache) { @@ -116,7 +123,8 @@ _xdg_mime_cache_new_from_file (const char *file_name) if (fd < 0) return NULL; - if (fstat (fd, &st) < 0 || st.st_size < 4) + // A valid cache must be at least 40 bytes for the header. + if (fstat (fd, &st) < 0 || st.st_size < 40) goto done; buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); @@ -209,6 +217,8 @@ cache_magic_matchlet_compare (XdgMimeCache *cache, { xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24); xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28); + if (OUT_OF_BOUNDS (child_offset, n_children, 32, cache->size)) + return FALSE; xdg_uint32_t i; @@ -239,6 +249,8 @@ cache_magic_compare_to_data (XdgMimeCache *cache, xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4); xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8); xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12); + if (OUT_OF_BOUNDS (matchlet_offset, n_matchlets, 32, cache->size)) + return NULL; xdg_uint32_t i; @@ -271,8 +283,13 @@ cache_magic_lookup_data (XdgMimeCache *cache, *prio = 0; list_offset = GET_UINT32 (cache->buffer, 24); + if (OUT_OF_BOUNDS (list_offset, 1, 12, cache->size)) + return NULL; + n_entries = GET_UINT32 (cache->buffer, list_offset); offset = GET_UINT32 (cache->buffer, list_offset + 8); + if (OUT_OF_BOUNDS (offset, n_entries, 16, cache->size)) + return NULL; for (j = 0; j < n_entries; j++) { @@ -304,7 +321,12 @@ cache_alias_lookup (const char *alias) continue; list_offset = GET_UINT32 (cache->buffer, 4); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size)) + continue; min = 0; max = n_entries - 1; @@ -356,7 +378,12 @@ cache_glob_lookup_literal (const char *file_name, continue; list_offset = GET_UINT32 (cache->buffer, 12); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 12, cache->size)) + continue; min = 0; max = n_entries - 1; @@ -417,7 +444,12 @@ cache_glob_lookup_fnmatch (const char *file_name, continue; list_offset = GET_UINT32 (cache->buffer, 20); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 12, cache->size)) + continue; for (j = 0; j < n_entries && n < n_mime_types; j++) { @@ -488,6 +520,8 @@ cache_glob_node_lookup_suffix (XdgMimeCache *cache, n = 0; n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4); child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8); + if (OUT_OF_BOUNDS (child_offset, n_children, 12, cache->size)) + continue; if (len > 0) { @@ -548,8 +582,13 @@ cache_glob_lookup_suffix (const char *file_name, continue; list_offset = GET_UINT32 (cache->buffer, 16); + if (OUT_OF_BOUNDS (list_offset, 1, 8, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); offset = GET_UINT32 (cache->buffer, list_offset + 4); + if (OUT_OF_BOUNDS (offset, n_entries, 12, cache->size)) + continue; n = cache_glob_node_lookup_suffix (cache, n_entries, offset, @@ -660,6 +699,9 @@ _xdg_mime_cache_get_max_buffer_extents (void) continue; offset = GET_UINT32 (cache->buffer, 24); + if (OUT_OF_BOUNDS (offset, 1, 8, cache->size)) + continue; + max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4)); } @@ -904,7 +946,12 @@ _xdg_mime_cache_mime_type_subclass (const char *mime, continue; list_offset = GET_UINT32 (cache->buffer, 8); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size)) + continue; min = 0; max = n_entries - 1; @@ -990,7 +1037,12 @@ _xdg_mime_cache_list_mime_parents (const char *mime) continue; list_offset = GET_UINT32 (cache->buffer, 8); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size)) + continue; for (j = 0; j < n_entries; j++) { @@ -1047,8 +1099,16 @@ cache_lookup_icon (const char *mime, int header) if (cache->buffer == NULL) continue; + if (OUT_OF_BOUNDS (header, 1, 4, cache->size)) + continue; + list_offset = GET_UINT32 (cache->buffer, header); + if (OUT_OF_BOUNDS (list_offset, 1, 4, cache->size)) + continue; + n_entries = GET_UINT32 (cache->buffer, list_offset); + if (OUT_OF_BOUNDS (list_offset + 4, n_entries, 8, cache->size)) + continue; min = 0; max = n_entries - 1; @@ -1103,6 +1163,9 @@ dump_glob_node (XdgMimeCache *cache, mime_offset = GET_UINT32 (cache->buffer, offset + 4); n_children = GET_UINT32 (cache->buffer, offset + 8); child_offset = GET_UINT32 (cache->buffer, offset + 12); + if (OUT_OF_BOUNDS (child_offset, n_children, 20, cache->size)) + return; + for (i = 0; i < depth; i++) printf (" "); printf ("%c", character); @@ -1131,8 +1194,14 @@ _xdg_mime_cache_glob_dump (void) continue; list_offset = GET_UINT32 (cache->buffer, 16); + if (OUT_OF_BOUNDS (list_offset, 1, 8, cache->size)) + return; + n_entries = GET_UINT32 (cache->buffer, list_offset); offset = GET_UINT32 (cache->buffer, list_offset + 4); + if (OUT_OF_BOUNDS (offset, n_entries, 20, cache->size)) + return; + for (j = 0; j < n_entries; j++) dump_glob_node (cache, offset + 20 * j, 0); } |