PR28240: debuginfod client root-safe negative caching
authorFrank Ch. Eigler <fche@redhat.com>
Wed, 18 Aug 2021 22:29:34 +0000 (18:29 -0400)
committerFrank Ch. Eigler <fche@redhat.com>
Fri, 22 Oct 2021 15:41:47 +0000 (11:41 -0400)
Negative cache (000-permission) files were incorrectly treated as
valid cached files for the root user, because root can open even
000-perm files without -EACCES.  Corrected this checking sequence.

Fixed the debuginfod testsuite to run to completion as root or
as an ordinary user, correcting corresponding permission checks:
    stat -c %A $FILE
is right and
    [ -w $FILE]  [ -r $FILE ]
were wrong.

Signed-off-by: Frank Ch. Eigler <fche@redhat.com>
debuginfod/ChangeLog
debuginfod/debuginfod-client.c
tests/ChangeLog
tests/run-debuginfod-000-permission.sh
tests/run-debuginfod-artifact-running.sh
tests/run-debuginfod-writable.sh

index a91749e..e0616b0 100644 (file)
@@ -1,3 +1,9 @@
+2021-10-23  Frank Ch. Eigler  <fche@redhat.com>
+
+       PR28240
+       * debuginfod-client.c (debuginfod_query_server): Correct
+       negative-hit cache check sequence for root user.
+
 2021-10-15  Mark Wielaard  <mark@klomp.org>
 
        * debuginfod-client.c (debuginfod_query_server): Set
index bd947ae..c875ee6 100644 (file)
@@ -1,5 +1,5 @@
 /* Retrieve ELF / DWARF / source files from the debuginfod.
-   Copyright (C) 2019-2020 Red Hat, Inc.
+   Copyright (C) 2019-2021 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -766,24 +766,14 @@ debuginfod_query_server (debuginfod_client *c,
   if (rc != 0)
     goto out;
 
-  /* If the target is already in the cache then we are done.  */
-  int fd = open (target_cache_path, O_RDONLY);
-  if (fd >= 0)
-    {
-      /* Success!!!! */
-      if (path != NULL)
-        *path = strdup(target_cache_path);
-      rc = fd;
-      goto out;
-    }
-
   struct stat st;
-  time_t cache_miss;
-  /* Check if the file exists and it's of permission 000*/
-  if (errno == EACCES
-      && stat(target_cache_path, &st) == 0
+  /* Check if the file exists and it's of permission 000; must check
+     explicitly rather than trying to open it first (PR28240). */
+  if (stat(target_cache_path, &st) == 0
       && (st.st_mode & 0777) == 0)
     {
+      time_t cache_miss;
+
       rc = debuginfod_config_cache(cache_miss_path, cache_miss_default_s, &st);
       if (rc < 0)
         goto out;
@@ -795,8 +785,23 @@ debuginfod_query_server (debuginfod_client *c,
          goto out;
        }
       else
+        /* TOCTOU non-problem: if another task races, puts a working
+           download or a 000 file in its place, unlinking here just
+           means WE will try to download again as uncached. */
         unlink(target_cache_path);
     }
+  
+  /* If the target is already in the cache (known not-000 - PR28240), 
+     then we are done. */
+  int fd = open (target_cache_path, O_RDONLY);
+  if (fd >= 0)
+    {
+      /* Success!!!! */
+      if (path != NULL)
+        *path = strdup(target_cache_path);
+      rc = fd;
+      goto out;
+    }
 
   long timeout = default_timeout;
   const char* timeout_envvar = getenv(DEBUGINFOD_TIMEOUT_ENV_VAR);
index 07e018b..46302b5 100644 (file)
@@ -1,3 +1,9 @@
+2021-10-23  Frank Ch. Eigler  <fche@redhat.com>
+
+       PR28240
+       * run-debuginfod-000-permission.sh, -writable.sh:
+       Correct negative-cache file permission checking.
+
 2021-10-06  Mark Wielaard  <mark@klomp.org>
 
        * show-die-info.c (handle): Handle dwarf_attr_string returning NULL.
index afa7e93..c1b2cf8 100755 (executable)
@@ -56,7 +56,7 @@ if [ ! -f $DEBUGINFOD_CACHE_PATH/01234567/debuginfo ]; then
   err
 fi
 
-if [ -r $DEBUGINFOD_CACHE_PATH/01234567/debuginfo ]; then
+if [ `stat -c "%A" $DEBUGINFOD_CACHE_PATH/01234567/debuginfo` != "----------" ]; then
   echo "The cache $DEBUGINFOD_CACHE_PATH/01234567/debuginfo is readable"
   err
 fi
index 51fa9c0..b944442 100755 (executable)
@@ -90,10 +90,6 @@ wait_ready $PORT1 'thread_busy{role="scan"}' 0
 rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID`
 cmp $filename F/prog.debug
-if [ -w $filename ]; then
-    echo "cache file writable, boo"
-    err
-fi
 
 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable F/prog`
 cmp $filename F/prog
index 69ececb..9cc4ea1 100755 (executable)
@@ -79,7 +79,7 @@ wait_ready $PORT1 'thread_busy{role="scan"}' 0
 rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests
 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID`
 cmp $filename F/p+r%o\$g.debug
-if [ -w $filename ]; then
+if [  `stat -c "%A" $filename` != "-r--------" ]; then
     echo "cache file writable, boo"
     err
 fi