summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--gtk/Makefile.am5
-rw-r--r--gtk/gtkiconcache.c25
-rw-r--r--gtk/gtkiconcachevalidator.c376
-rw-r--r--gtk/gtkiconcachevalidator.h44
-rw-r--r--gtk/updateiconcache.c63
6 files changed, 513 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index 0d887d4ad3..708cf4a6d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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"