summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/xdgmimecache.c71
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);
}