Fromr: Stephen Kitt <steve@sk2.org>
Subject: Fix crashes related to invalid CABs

Jakub Wilk pointed out that AFL (http://lcamtuf.coredump.cx/afl/)
finds quite a few crashes caused by invalid CABs. They're mostly due
to either missing input validation or error paths which don't fill in
error information; the attached patch fixes these.

With the patch applied AFL no longer finds any crashes.

Bug-Debian: https://bugs.debian.org/775941

--- a/libgcab/gcab-cabinet.c
+++ b/libgcab/gcab-cabinet.c
@@ -384,8 +384,20 @@
         if (!cfile_read (&cfile, in, cancellable, error))
             goto end;
 
-        GCabFile *file = gcab_file_new_with_cfile (&cfile);
+        if (cfile.index >= folders->len) {
+            g_set_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT,
+                         "Invalid folder index");
+            goto end;
+        }
+
         GCabFolder *folder = g_ptr_array_index (folders, cfile.index);
+        if (folder == NULL) {
+            g_set_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT,
+                         "Invalid folder pointer");
+            goto end;
+        }
+
+        GCabFile *file = gcab_file_new_with_cfile (&cfile);
         if (!gcab_folder_add_file (folder, file, FALSE, cancellable, error)) {
             g_object_unref (file);
             goto end;
--- a/libgcab/cabinet.c
+++ b/libgcab/cabinet.c
@@ -96,14 +96,24 @@
 #define RS(val) G_STMT_START{                                   \
     val = _data_input_stream_read_until (in, "\0", 1,           \
                                          cancellable, error);   \
-    if (!val || (error && *error))                              \
+    if (error && *error)                                        \
         goto end;                                               \
+    if (!val) {                                                 \
+        g_set_error(error, GCAB_ERROR, GCAB_ERROR_FORMAT,       \
+                    "Invalid contents");                        \
+        goto end;                                               \
+    }                                                           \
 }G_STMT_END
 #define RN(buff, size) G_STMT_START{                                    \
     if (size) {                                                         \
         gint _val = g_input_stream_read (G_INPUT_STREAM (in), buff, size, cancellable, error); \
-        if (_val == -1 || (error && *error))                            \
+        if (error && *error)                                            \
+            goto end;                                                   \
+        if (_val == -1) {                                               \
+            g_set_error(error, GCAB_ERROR, GCAB_ERROR_FORMAT,           \
+                        "Invalid contents");                            \
             goto end;                                                   \
+	}                                                               \
     }                                                                   \
 }G_STMT_END
 
--- a/libgcab/gcab-folder.c
+++ b/libgcab/gcab-folder.c
@@ -143,10 +143,13 @@
 }
 
 static gboolean
-add_file (GCabFolder *self, GCabFile *file)
+add_file (GCabFolder *self, GCabFile *file, GError **error)
 {
-    if (g_hash_table_lookup (self->hash, (gpointer)gcab_file_get_name (file)))
+    if (g_hash_table_lookup (self->hash, (gpointer)gcab_file_get_name (file))) {
+        g_set_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT,
+                     "File '%s' has already been added", gcab_file_get_name (file));
         return FALSE;
+    }
 
     g_hash_table_insert (self->hash,
                          (gpointer)gcab_file_get_name (file), g_object_ref (file));
@@ -196,7 +199,7 @@
 
     } else if (file_type == G_FILE_TYPE_REGULAR) {
         gcab_file_update_info (file, info);
-        if (!add_file (self, file))
+        if (!add_file (self, file, error))
             return FALSE;
 
     } else {
@@ -244,7 +247,7 @@
                                  gcab_file_get_name (file), recurse, error);
         g_object_unref (info);
     } else {
-        success = add_file (self, file);
+        success = add_file (self, file, error);
     }
 
     return success;
