lib/prune: Don't modify dirent->d_name in place
authorMatthew Leeds <matthew.leeds@endlessm.com>
Thu, 14 Jun 2018 21:33:10 +0000 (14:33 -0700)
committerAtomic Bot <atomic-devel@projectatomic.io>
Fri, 15 Jun 2018 19:01:46 +0000 (19:01 +0000)
Currently when I run `ostree prune` it hits a seg fault when the
hash_func is used (in this case g_str_hash) from the call stack
_ostree_repo_prune_tmp() -> g_hash_table_contains() ->
g_hash_table_lookup_node(). So the key, in this case dent->d_name, must
be corrupt in some way.

glnx_dirfd_iterator_next_dent() uses readdir() to get the dirent struct.
And according to the man page for readdir(3), "POSIX.1 explicitly notes
that this field should not be used as an lvalue" (in reference to
d_name). So this commit avoids modifying d_name in place and copies it
instead. This seems to avoid the seg fault.

Closes: #1627
Approved by: jlebon

src/libostree/ostree-repo-prune.c

index b93d35acf6c1efabc5c5d57231c05ebdc03a7e8e..7ab9dc8dba47e12d7916a78e756c0e389c80b916 100644 (file)
@@ -153,26 +153,29 @@ _ostree_repo_prune_tmp (OstreeRepo *self,
       size_t len;
       gboolean has_sig_suffix = FALSE;
       struct dirent *dent;
+      g_autofree gchar *d_name = NULL;
 
       if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
         return FALSE;
       if (dent == NULL)
         break;
 
-      len = strlen (dent->d_name);
-      if (len > 4 && g_strcmp0 (dent->d_name + len - 4, ".sig") == 0)
+      /* dirent->d_name can't be modified directly; see `man 3 readdir` */
+      d_name = g_strdup (dent->d_name);
+      len = strlen (d_name);
+      if (len > 4 && g_strcmp0 (d_name + len - 4, ".sig") == 0)
         {
           has_sig_suffix = TRUE;
-          dent->d_name[len - 4] = '\0';
+          d_name[len - 4] = '\0';
         }
 
-      if (!g_hash_table_contains (self->remotes, dent->d_name))
+      if (!g_hash_table_contains (self->remotes, d_name))
         {
           /* Restore the previous value to get the file name.  */
           if (has_sig_suffix)
-            dent->d_name[len - 4] = '.';
+            d_name[len - 4] = '.';
 
-          if (!glnx_unlinkat (dfd_iter.fd, dent->d_name, 0, error))
+          if (!glnx_unlinkat (dfd_iter.fd, d_name, 0, error))
             return FALSE;
         }
     }