[gobject-introspection] girepository: avoid crash when querying nonexistent info



commit d055d35a873be5978d82df3955d859399c2e30d9
Author: Pavel Holejsovsky <pavel holejsovsky gmail com>
Date:   Sat May 12 07:44:57 2012 +0200

    girepository: avoid crash when querying nonexistent info
    
    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 |    2 +-
 girepository/gitypelib.c          |    6 +++---
 girepository/gthash-test.c        |    8 ++++----
 girepository/gthash.c             |   10 +++++++++-
 tests/repository/gitypelibtest.c  |   11 +++++++++++
 5 files changed, 28 insertions(+), 9 deletions(-)
---
diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h
index ed8e679..04662b4 100644
--- a/girepository/gitypelib-internal.h
+++ b/girepository/gitypelib-internal.h
@@ -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
diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c
index ae6b845..2af17e9 100644
--- a/girepository/gitypelib.c
+++ b/girepository/gitypelib.c
@@ -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)
diff --git a/girepository/gthash-test.c b/girepository/gthash-test.c
index 7909a0c..ea811e3 100644
--- a/girepository/gthash-test.c
+++ b/girepository/gthash-test.c
@@ -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
diff --git a/girepository/gthash.c b/girepository/gthash.c
index 8a35295..b50ea6f 100644
--- a/girepository/gthash.c
+++ b/girepository/gthash.c
@@ -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);
 
diff --git a/tests/repository/gitypelibtest.c b/tests/repository/gitypelibtest.c
index c53eab2..7068ef3 100644
--- a/tests/repository/gitypelibtest.c
+++ b/tests/repository/gitypelibtest.c
@@ -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);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]