diff options
-rw-r--r-- | ChangeLog | 12 | ||||
-rw-r--r-- | gtk/Makefile.am | 5 | ||||
-rw-r--r-- | gtk/gtkiconcache.c | 25 | ||||
-rw-r--r-- | gtk/gtkiconcachevalidator.c | 376 | ||||
-rw-r--r-- | gtk/gtkiconcachevalidator.h | 44 | ||||
-rw-r--r-- | gtk/updateiconcache.c | 63 |
6 files changed, 513 insertions, 12 deletions
@@ -1,3 +1,15 @@ +2007-05-01 Matthias Clasen <mclasen@redhat.com> + + * gtk/gtkiconcachvalidator.[hc]: Add an icon cache validator. + + * gtk/updateiconcache.c: Validate the generated cache before + moving it in place. Also add a --validate option to validate + an existing icon cache. + + * gtk/gtkiconcache.c: Validate icon caches before using them. + + * gtk/Makefile.am: Integrate it. + 2007-05-01 Michael Emmel <mike.emmel@gmail.com> * gdk/directfb/gdkdisplay-directfb.c: diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 74e8884c58..72481ba87c 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -454,6 +454,7 @@ gtk_base_c_sources = \ gtkhseparator.c \ gtkhsv.c \ gtkiconcache.c \ + gtkiconcachevalidator.c \ gtkiconfactory.c \ gtkicontheme.c \ gtkiconview.c \ @@ -610,6 +611,7 @@ gtk_all_c_sources += $(gtk_os_unix_c_sources) if OS_UNIX gtk_private_h_sources += \ gtkfilesystemunix.h \ + gtkiconcachevalidator.h \ gtkprintbackend.h \ gtkprinter-private.h \ gtkprinteroption.h \ @@ -865,7 +867,8 @@ gtk_query_immodules_2_0_SOURCES = queryimmodules.c gtk_update_icon_cache_LDADD = $(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la -gtk_update_icon_cache_SOURCES = updateiconcache.c +gtk_update_icon_cache_SOURCES = \ + updateiconcache.c .PHONY: files test test-debug diff --git a/gtk/gtkiconcache.c b/gtk/gtkiconcache.c index a15d8d39c3..cd25d300f1 100644 --- a/gtk/gtkiconcache.c +++ b/gtk/gtkiconcache.c @@ -21,6 +21,7 @@ #include "gtkdebug.h" #include "gtkiconcache.h" +#include "gtkiconcachevalidator.h" #include "gtkalias.h" #include <glib/gstdio.h> @@ -58,7 +59,7 @@ struct _GtkIconCache { GtkIconCache * _gtk_icon_cache_ref (GtkIconCache *cache) { - cache->ref_count ++; + cache->ref_count++; return cache; } @@ -89,6 +90,7 @@ _gtk_icon_cache_new_for_path (const gchar *path) struct stat st; struct stat path_st; gchar *buffer = NULL; + CacheInfo info; /* Check if we have a cache file */ cache_filename = g_build_filename (path, "icon-theme.cache", NULL); @@ -121,25 +123,26 @@ _gtk_icon_cache_new_for_path (const gchar *path) if (!map) goto done; - /* Verify version */ - buffer = g_mapped_file_get_contents (map); - if (GET_UINT16 (buffer, 0) != MAJOR_VERSION || - GET_UINT16 (buffer, 2) != MINOR_VERSION) + info.cache = g_mapped_file_get_contents (map); + info.cache_size = g_mapped_file_get_length (map); + info.n_directories = 0; + info.flags = CHECK_OFFSETS|CHECK_STRINGS; + + g_print ("validating %s\n", cache_filename); + if (!_gtk_icon_cache_validate (&info)) { g_mapped_file_free (map); - GTK_NOTE (ICONTHEME, - g_print ("wrong cache version\n")); + GTK_NOTE (ICONTHEME, g_print ("invalid icon cache\n")); goto done; } - GTK_NOTE (ICONTHEME, - g_print ("found cache for %s\n", path)); + GTK_NOTE (ICONTHEME, g_print ("found cache for %s\n", path)); cache = g_new0 (GtkIconCache, 1); cache->ref_count = 1; cache->map = map; - cache->buffer = buffer; + cache->buffer = g_mapped_file_get_contents (map); done: g_free (cache_filename); @@ -440,7 +443,7 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache, length = GET_UINT32 (cache->buffer, pixel_data_offset + 4); if (!gdk_pixdata_deserialize (&pixdata, length, - cache->buffer + pixel_data_offset + 8, + (guchar *)(cache->buffer + pixel_data_offset + 8), &error)) { GTK_NOTE (ICONTHEME, diff --git a/gtk/gtkiconcachevalidator.c b/gtk/gtkiconcachevalidator.c new file mode 100644 index 0000000000..cb1d964ba7 --- /dev/null +++ b/gtk/gtkiconcachevalidator.c @@ -0,0 +1,376 @@ +/* gtkiconcachevalidator.c + * Copyright (C) 2007 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "config.h" +#include "gtkiconcachevalidator.h" + +#include <glib.h> +#include <gdk-pixbuf/gdk-pixdata.h> + + +#define VERBOSE(x) + +#define check(name,condition) \ + if (!(condition)) \ + { \ + VERBOSE(g_print ("bad %s\n", (name))); \ + return FALSE; \ + } + +static inline gboolean +get_uint16 (CacheInfo *info, + guint32 offset, + guint16 *value) +{ + if (offset < info->cache_size) + { + *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset)); + return TRUE; + } + else + { + *value = 0; + return FALSE; + } +} + +static inline gboolean +get_uint32 (CacheInfo *info, + guint32 offset, + guint32 *value) +{ + if (offset < info->cache_size) + { + *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset)); + return TRUE; + } + else + { + *value = 0; + return FALSE; + } +} + +static gboolean +check_version (CacheInfo *info) +{ + guint16 major, minor; + + check ("major version", get_uint16 (info, 0, &major) && major == 1); + check ("minor version", get_uint16 (info, 2, &minor) && minor == 0); + + return TRUE; +} + +static gboolean +check_string (CacheInfo *info, + guint32 offset) +{ + check ("string offset", offset < info->cache_size); + + if (info->flags & CHECK_STRINGS) + { + gint i; + gchar c; + + /* assume no string is longer than 1k */ + for (i = 0; i < 1024; i++) + { + check ("string offset", offset + i < info->cache_size) + c = *(info->cache + offset + i); + if (c == '\0') + break; + check ("string content", g_ascii_isgraph (c)); + } + check ("string length", i < 1024); + } + + return TRUE; +} + +static gboolean +check_directory_list (CacheInfo *info, + guint32 offset) +{ + guint32 directory_offset; + gint i; + + check ("offset, directory list", get_uint32 (info, offset, &info->n_directories)); + + for (i = 0; i < info->n_directories; i++) + { + check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset)); + if (!check_string (info, directory_offset)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_pixel_data (CacheInfo *info, + guint32 offset) +{ + guint32 type; + guint32 length; + + check ("offset, pixel data type", get_uint32 (info, offset, &type)); + check ("offset, pixel data length", get_uint32 (info, offset + 4, &length)); + + check ("pixel data type", type == 0); + check ("pixel data length", offset + 8 + length < info->cache_size); + + if (info->flags & CHECK_PIXBUFS) + { + GdkPixdata data; + + check ("pixel data", gdk_pixdata_deserialize (&data, length, + info->cache + offset + 8, + NULL)); + } + + return TRUE; +} + +static gboolean +check_embedded_rect (CacheInfo *info, + guint32 offset) +{ + check ("embedded rect", offset + 4 < info->cache_size); + + return TRUE; +} + +static gboolean +check_attach_point_list (CacheInfo *info, + guint32 offset) +{ + guint32 n_attach_points; + + check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points)); + check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size); + + return TRUE; +} + +static gboolean +check_display_name_list (CacheInfo *info, + guint32 offset) +{ + guint32 n_display_names; + gint i; + + check ("offset, display name list", + get_uint32 (info, offset, &n_display_names)); + for (i = 0; i < n_display_names; i++) + { + if (!check_string (info, offset + 4 + 8 * i)) + return FALSE; + if (!check_string (info, offset + 4 + 8 * i + 4)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_meta_data (CacheInfo *info, + guint32 offset) +{ + guint32 embedded_rect_offset; + guint32 attach_point_list_offset; + guint32 display_name_list_offset; + + check ("offset, embedded rect", + get_uint32 (info, offset, &embedded_rect_offset)); + check ("offset, attach point list", + get_uint32 (info, offset + 4, &attach_point_list_offset)); + check ("offset, display name list", + get_uint32 (info, offset + 8, &display_name_list_offset)); + + if (embedded_rect_offset != 0) + { + if (!check_embedded_rect (info, embedded_rect_offset)) + return FALSE; + } + + if (attach_point_list_offset != 0) + { + if (!check_attach_point_list (info, attach_point_list_offset)) + return FALSE; + } + + if (display_name_list_offset != 0) + { + if (!check_display_name_list (info, display_name_list_offset)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_image_data (CacheInfo *info, + guint32 offset) +{ + guint32 pixel_data_offset; + guint32 meta_data_offset; + + check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset)); + check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset)); + + if (pixel_data_offset != 0) + { + if (!check_pixel_data (info, pixel_data_offset)) + return FALSE; + } + if (meta_data_offset != 0) + { + if (!check_meta_data (info, meta_data_offset)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_image (CacheInfo *info, + guint32 offset) +{ + guint16 index; + guint16 flags; + guint32 image_data_offset; + + check ("offset, image index", get_uint16 (info, offset, &index)); + check ("offset, image flags", get_uint16 (info, offset + 2, &flags)); + check ("offset, image data offset", + get_uint32 (info, offset + 4, &image_data_offset)); + + check ("image index", index < info->n_directories); + check ("image flags", flags == 1 || flags == 2 || flags == 4 || + flags == 9 || flags == 10 || flags == 12); + + if (image_data_offset != 0) + { + if (!check_image_data (info, image_data_offset)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_image_list (CacheInfo *info, + guint32 offset) +{ + guint32 n_images; + gint i; + + check ("offset, image list", get_uint32 (info, offset, &n_images)); + + for (i = 0; i < n_images; i++) + { + if (!check_image (info, offset + 4 + 8 * i)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_icon (CacheInfo *info, + guint32 offset) +{ + guint32 chain_offset; + guint32 name_offset; + guint32 image_list_offset; + + check ("offset, icon chain", get_uint32 (info, offset, &chain_offset)); + check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset)); + check ("offset, icon image list", get_uint32 (info, offset + 8, + &image_list_offset)); + + if (!check_string (info, name_offset)) + return FALSE; + if (!check_image_list (info, image_list_offset)) + return FALSE; + if (chain_offset != 0xffffffff) + { + if (!check_icon (info, chain_offset)) + return FALSE; + } + + return TRUE; +} + +static gboolean +check_hash (CacheInfo *info, + guint32 offset) +{ + guint32 n_buckets, icon_offset; + gint i; + + check ("offset, hash size", get_uint32 (info, offset, &n_buckets)); + + for (i = 0; i < n_buckets; i++) + { + check ("offset, hash chain", + get_uint32 (info, offset + 4 + 4 * i, &icon_offset)); + if (icon_offset != 0xffffffff) + { + if (!check_icon (info, icon_offset)) + return FALSE; + } + } + + return TRUE; +} + +/** + * _gtk_icon_cache_validate: + * @info: a CacheInfo structure + * + * Validates the icon cache passed in the @cache and + * @cache_size fields of the @info structure. The + * validator checks that offsets specified in the + * cache do not point outside the mapped area, that + * strings look reasonable, and that pixbufs can + * be deserialized. The amount of validation can + * be controlled with the @flags field. + * + * Return value: %TRUE if the cache is valid + */ +gboolean +_gtk_icon_cache_validate (CacheInfo *info) +{ + guint32 hash_offset; + guint32 directory_list_offset; + + if (!check_version (info)) + return FALSE; + check ("header, hash offset", get_uint32 (info, 4, &hash_offset)); + check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset)); + if (!check_directory_list (info, directory_list_offset)) + return FALSE; + + if (!check_hash (info, hash_offset)) + return FALSE; + + return TRUE; +} + diff --git a/gtk/gtkiconcachevalidator.h b/gtk/gtkiconcachevalidator.h new file mode 100644 index 0000000000..9b22e85ed9 --- /dev/null +++ b/gtk/gtkiconcachevalidator.h @@ -0,0 +1,44 @@ +/* gtkiconcachevalidator.4 + * Copyright (C) 2007 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GTK_ICON_CACHE_VALIDATOR_H__ +#define __GTK_ICON_CACHE_VALIDATOR_H__ + + +#include <glib.h> + +G_BEGIN_DECLS + +enum { + CHECK_OFFSETS = 1, + CHECK_STRINGS = 2, + CHECK_PIXBUFS = 4 +}; + +typedef struct { + const gchar *cache; + gsize cache_size; + guint32 n_directories; + gint flags; +} CacheInfo; + +gboolean _gtk_icon_cache_validate (CacheInfo *info); + +G_END_DECLS + +#endif /* __GTK_ICON_CACHE_VALIDATOR_H__ */ diff --git a/gtk/updateiconcache.c b/gtk/updateiconcache.c index cdd6db3b10..d11f11f909 100644 --- a/gtk/updateiconcache.c +++ b/gtk/updateiconcache.c @@ -39,13 +39,20 @@ #include <glib/gstdio.h> #include <gdk-pixbuf/gdk-pixdata.h> #include <glib/gi18n.h> +#include "gtkiconcachevalidator.h" static gboolean force_update = FALSE; static gboolean ignore_theme_index = FALSE; static gboolean quiet = FALSE; static gboolean index_only = FALSE; +static gboolean validate = FALSE; static gchar *var_name = "-"; +/* Quite ugly - if we just add the c file to the + * list of sources in Makefile.am, libtool complains. + */ +#include "gtkiconcachevalidator.c" + #define CACHE_NAME "icon-theme.cache" #define HAS_SUFFIX_XPM (1 << 0) @@ -1396,6 +1403,32 @@ write_file (FILE *cache, GHashTable *files, GList *directories) return TRUE; } +static gboolean +validate_file (const gchar *file) +{ + GMappedFile *map; + CacheInfo info; + + map = g_mapped_file_new (file, FALSE, NULL); + if (!map) + return FALSE; + + info.cache = g_mapped_file_get_contents (map); + info.cache_size = g_mapped_file_get_length (map); + info.n_directories = 0; + info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS; + + if (!_gtk_icon_cache_validate (&info)) + { + g_mapped_file_free (map); + return FALSE; + } + + g_mapped_file_free (map); + + return TRUE; +} + static void build_cache (const gchar *path) { @@ -1448,6 +1481,13 @@ build_cache (const gchar *path) exit (1); } + if (!validate_file (tmp_cache_path)) + { + g_printerr (_("The generated cache was invalid.\n")); + g_unlink (tmp_cache_path); + exit (1); + } + cache_path = g_build_filename (path, CACHE_NAME, NULL); #ifdef G_OS_WIN32 @@ -1542,6 +1582,7 @@ static GOptionEntry args[] = { { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL }, { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" }, { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL }, + { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL }, { NULL } }; @@ -1569,6 +1610,28 @@ main (int argc, char **argv) path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL); #endif + if (validate) + { + gchar *file = g_build_filename (path, CACHE_NAME, NULL); + + if (!g_file_test (file, G_FILE_TEST_IS_REGULAR)) + { + if (!quiet) + g_printerr (_("File not found: %s\n"), file); + exit (1); + } + if (!validate_file (file)) + { + if (!quiet) + g_printerr (_("Not a valid icon cache: %s\n"), file); + exit (1); + } + else + { + exit (0); + } + } + if (!ignore_theme_index && !has_theme_index (path)) { g_printerr (_("No theme index file in '%s'.\n" |