girepository: avoid crash when querying nonexistent info
authorPavel Holejsovsky <pavel.holejsovsky@gmail.com>
Sat, 12 May 2012 05:44:57 +0000 (07:44 +0200)
committerPavel Holejsovsky <pavel.holejsovsky@gmail.com>
Sat, 12 May 2012 18:48:55 +0000 (20:48 +0200)
It appears that cmph library can return (n+1) when querying item not
present in its original n-item-sized set.  Adjust code so that it
detects this condition and do not chase stray pointers resulting from
this bogus(?) hash result.

https://bugzilla.gnome.org/show_bug.cgi?id=675939

girepository/gitypelib-internal.h
girepository/gitypelib.c
girepository/gthash-test.c
girepository/gthash.c
tests/repository/gitypelibtest.c

index ed8e679..04662b4 100644 (file)
@@ -1183,7 +1183,7 @@ void _gi_typelib_hash_builder_pack (GITypelibHashBuilder *builder, guint8* mem,
 
 void _gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder);
 
-guint16 _gi_typelib_hash_search (guint8* memory, const char *str);
+guint16 _gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries);
 
 
 G_END_DECLS
index ae6b845..2af17e9 100644 (file)
@@ -165,15 +165,15 @@ g_typelib_get_dir_entry_by_name (GITypelib *typelib,
                                 const char *name)
 {
   Section *dirindex;
-  gint i;
+  gint i, n_entries;
   const char *entry_name;
   DirEntry *entry;
 
   dirindex = get_section_by_id (typelib, GI_SECTION_DIRECTORY_INDEX);
+  n_entries = ((Header *)typelib->data)->n_local_entries;
 
   if (dirindex == NULL)
     {
-      gint n_entries = ((Header *)typelib->data)->n_local_entries;
       for (i = 1; i <= n_entries; i++)
        {
          entry = g_typelib_get_dir_entry (typelib, i);
@@ -188,7 +188,7 @@ g_typelib_get_dir_entry_by_name (GITypelib *typelib,
       guint8 *hash = (guint8*) &typelib->data[dirindex->offset];
       guint16 index;
 
-      index = _gi_typelib_hash_search (hash, name);
+      index = _gi_typelib_hash_search (hash, name, n_entries);
       entry = g_typelib_get_dir_entry (typelib, index + 1);
       entry_name = g_typelib_get_string (typelib, entry->name);
       if (strcmp (name, entry_name) == 0)
index 7909a0c..ea811e3 100644 (file)
@@ -47,10 +47,10 @@ test_build_retrieve (void)
 
   _gi_typelib_hash_builder_destroy (builder);
 
-  g_assert (_gi_typelib_hash_search (buf, "Action") == 0);
-  g_assert (_gi_typelib_hash_search (buf, "ZLibDecompressor") == 42);
-  g_assert (_gi_typelib_hash_search (buf, "VolumeMonitor") == 9);
-  g_assert (_gi_typelib_hash_search (buf, "FileMonitorFlags") == 31);
+  g_assert (_gi_typelib_hash_search (buf, "Action", 4) == 0);
+  g_assert (_gi_typelib_hash_search (buf, "ZLibDecompressor", 4) == 42);
+  g_assert (_gi_typelib_hash_search (buf, "VolumeMonitor", 4) == 9);
+  g_assert (_gi_typelib_hash_search (buf, "FileMonitorFlags", 4) == 31);
 }
 
 int
index 8a35295..b50ea6f 100644 (file)
@@ -191,7 +191,7 @@ _gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder)
 }
 
 guint16
-_gi_typelib_hash_search (guint8* memory, const char *str)
+_gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries)
 {
   guint32 *mph;
   guint16 *table;
@@ -203,6 +203,14 @@ _gi_typelib_hash_search (guint8* memory, const char *str)
 
   offset = cmph_search_packed (mph, str, strlen (str));
 
+  /* Make sure that offset always lies in the entries array.  cmph
+     cometimes generates offset larger than number of entries (for
+     'str' argument which is not in the hashed list). In this case,
+     fake the correct result and depend on caller's final check that
+     the entry is really the one that the caller wanted. */
+  if (offset >= n_entries)
+    offset = 0;
+
   dirmap_offset = *((guint32*)memory);
   table = (guint16*) (memory + dirmap_offset);
 
index c53eab2..7068ef3 100644 (file)
@@ -168,6 +168,16 @@ test_fundamental_get_ref_function_pointer (GIRepository *repo)
     g_base_info_unref (info);
 }
 
+static void
+test_hash_with_cairo_typelib (GIRepository *repo)
+{
+    GIBaseInfo *info;
+
+    g_assert (g_irepository_require (repo, "cairo", NULL, 0, NULL));
+    info = g_irepository_find_by_name (repo, "cairo", "region");
+    g_assert (info == NULL);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -183,6 +193,7 @@ main(int argc, char **argv)
     test_size_of_gvalue (repo);
     test_is_pointer_for_struct_arg (repo);
     test_fundamental_get_ref_function_pointer (repo);
+    test_hash_with_cairo_typelib (repo);
 
     exit(0);
 }