Bug 11149

Summary: ldconfig - auxiliary cache does not work properly with -r option
Product: glibc Reporter: Vasily Gurevich <vas.gurevich>
Component: libcAssignee: Ulrich Drepper <drepper.fsp>
Status: RESOLVED FIXED    
Severity: normal CC: anssi.hannula, glibc-bugs
Priority: P2 Flags: fweimer: security-
Version: 2.11   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: proposed patch

Description Vasily Gurevich 2010-01-08 18:38:22 UTC
There are 2 bugs here:

When we use ldconfig with -r option like usual user we use absolute path to
aux-cache file without any changes according to the new root.

When we use ldconfig with -r option like usual user we do not check if the
symbolic link in our new root points outside of our root and we need "normalize"
the name according to -r option.
(We have a link to /lib/ourlib and stat this link instead of
/our_new_root/lib/ourlib)

I am proposing the patch against 2.11.1 here:

--- orig/glibc-2.11.1/elf/ldconfig.c    2009-12-08 22:10:20.000000000 +0200
+++ patch/glibc-2.11.1/elf/ldconfig.c   2010-01-08 18:10:32.000000000 +0200
@@ -118,6 +118,9 @@
 /* Cache file to use.  */
 static char *cache_file;

+/* Aux cache file to use.  */
+static char *aux_cache_file;
+
 /* Configuration file.  */
 static const char *config_file;

@@ -790,6 +793,34 @@
          lstat_buf.st_ino = stat_buf.st_ino;
          lstat_buf.st_size = stat_buf.st_size;
          lstat_buf.st_ctime = stat_buf.st_ctime;
+
+         /* We need to check if it is link outside of new root
+            and stat returns to us different info than real chroot.  */
+          if (opt_chroot)
+           {
+             char *real_name = chroot_canon (opt_chroot, file_name);
+             if (real_name == NULL)
+               {
+                 if (strstr (file_name, ".so") == NULL)
+                   error (0, 0, _("Input file %s not found.\n"), file_name);
+                 continue;
+               }
+             if (__builtin_expect (stat64 (real_name, &stat_buf), 0))
+               {
+                 error (0, errno, _("Cannot stat %s"), real_name);
+                 continue;
+               }
+             if (lstat_buf.st_dev != stat_buf.st_dev
+                 || lstat_buf.st_ino != stat_buf.st_ino
+                 || lstat_buf.st_size != stat_buf.st_size
+                 || lstat_buf.st_ctime != stat_buf.st_ctime)
+               {
+                 lstat_buf.st_dev = stat_buf.st_dev;
+                 lstat_buf.st_ino = stat_buf.st_ino;
+                 lstat_buf.st_size = stat_buf.st_size;
+                 lstat_buf.st_ctime = stat_buf.st_ctime;
+               }
+           }
        }
       else
        is_dir = S_ISDIR (lstat_buf.st_mode);
@@ -1346,8 +1377,39 @@
        add_system_dir (LIBDIR);
     }

+  if (aux_cache_file == NULL)
+    {
+      aux_cache_file = alloca (strlen (_PATH_LDCONFIG_AUX_CACHE) + 1);
+      strcpy (aux_cache_file, _PATH_LDCONFIG_AUX_CACHE);
+    }
+
+  if (opt_chroot)
+    {
+      /* Canonicalize the directory name of aux_cache_file, not aux_cache_file,
+        because we'll rename a temporary aux cache file to it.  */
+      char *p = strrchr (aux_cache_file, '/');
+      char *canon = chroot_canon (opt_chroot,
+                                 p ? (*p = '\0', aux_cache_file) : "/");
+
+      if (canon == NULL)
+       {
+         error (EXIT_FAILURE, errno,
+                _("Can't open aux cache file directory %s\n"),
+                p ? aux_cache_file : "/");
+       }
+
+      if (p)
+       ++p;
+      else
+       p = aux_cache_file;
+
+      aux_cache_file = alloca (strlen (canon) + strlen (p) + 2);
+      sprintf (aux_cache_file, "%s/%s", canon, p);
+      free (canon);
+    }
+
   if (! opt_ignore_aux_cache)
-    load_aux_cache (_PATH_LDCONFIG_AUX_CACHE);
+    load_aux_cache (aux_cache_file);
   else
     init_aux_cache ();

@@ -1356,7 +1418,7 @@
   if (opt_build_cache)
     {
       save_cache (cache_file);
-      save_aux_cache (_PATH_LDCONFIG_AUX_CACHE);
+      save_aux_cache (aux_cache_file);
     }

   return 0;

I checked the newest 2.12, we still have it.
Comment 1 Ulrich Drepper 2010-04-05 18:27:55 UTC
I agree with the aux cache file setting (although I used a simpler patch).

You will have to explain the second part.  Describe the setup you think creates
a problem.
Comment 2 Vasily Gurevich 2010-04-09 15:59:54 UTC
I use maemo SDK+. It does not use chroot. I launch ldconfig -r /our_new_root. It
finds link to /lib/ourlib inside /our_new_root and tries to stat /lib/ourlib.
But we specify -r /our_new_root. So it shall stat /our_new_root/lib/ourlib
according to -r options. My patch provides this functionality. Thank you.
Comment 3 Ulrich Drepper 2010-04-09 16:34:15 UTC
(In reply to comment #2)
> I use maemo SDK+. It does not use chroot. I launch ldconfig -r /our_new_root. It
> finds link to /lib/ourlib inside /our_new_root and tries to stat /lib/ourlib.

Explain exactly where the link is located etc.  I need to be able to reproduce
this here.
Comment 4 Vasily Gurevich 2010-04-09 17:25:51 UTC
#pwd
/lib
#ls -la
somelib
somelib.ln -> somelib

#pwd
~/our_new_root/lib
#ls -la
somelib
somelib.ln -> somelib

ldconfig searchs it in aux cache (search_aux_cache), does not find, tries to add
(add_to_aux_cache) and aborts when tries to insert.

Please, ask me if it is not enough. Thank you.
Comment 5 Ulrich Drepper 2010-04-10 13:43:12 UTC
(In reply to comment #4)
> Please, ask me if it is not enough. Thank you.

It isn't enough.  First, files named somelib are ignored because they don't look
like libraries.  I asked for the exact situation, not some general idea.

Second, even if I'm using a real library with its real name and then a link I
see no accesses outside the chroot:

I copied libc.so in a chroot tree and added libc.so.6 as a symlink.  The only
accesses to libc.so of

ldconfig -r $PWD/testroot

are:

lstat("$PWD/testroot/lib64/libc.so", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
open("$PWD/testroot/lib64/libc.so", O_RDONLY) = 4
stat("$PWD/testroot/lib64/libc.so.6", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
lstat("$PWD/testroot/lib64/libc.so.6", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
readlink("$PWD/testroot/lib64/libc.so.6", "libc.so", 4096) = 7
lstat("$PWD/testroot/lib64/libc.so", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
lstat("$PWD/testroot/lib64/libc.so.6", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
lstat("$PWD/testroot/lib64/libc.so.6", {st_mode=S_IFLNK|0777, st_size=7, ...}) = 0
readlink("$PWD/testroot/lib64/libc.so.6", "libc.so", 4096) = 7
lstat("$PWD/testroot/lib64/libc.so", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
stat("$PWD/testroot/lib64/libc.so", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
lstat("$PWD/testroot/lib64/libc.so", {st_mode=S_IFREG|0775, st_size=9166760,
...}) = 0
write(3, "libc.so.6\0/lib64/libc.so.6\0", 27) = 27

I have of course replace the real path with $PWD in the output.  But as you can
see there is no access outside the chroot.


Either provide _exact_ instructions for how to show the problem or close the
bug.  And make sure you're using current sources.
Comment 6 Vasily Gurevich 2010-04-12 17:27:54 UTC
I am sorry. I built glibc-2.11.1 on top of my ubuntu jaunty. There are minimum
settings:

# pwd
/home/user/testroot
# find
.
./lib
./lib/libBrokenLocale.so.1
./lib/libBrokenLocale-2.8.so
./etc
./etc/ld.so.conf
./var
./var/cache
./usr
./usr/lib
./usr/lib/libBrokenLocale.so
# pwd
/home/user/testroot/lib
# ls -l
-rw-r--r-- 1 user user 5484 2010-02-23 15:55 libBrokenLocale-2.8.so
lrwxrwxrwx 1 user user   22 2010-04-12 20:27 libBrokenLocale.so.1 ->
libBrokenLocale-2.8.so
# pwd
/home/user/testroot/usr/lib
# ls -l
lrwxrwxrwx 1 user user 25 2010-04-12 20:26 libBrokenLocale.so ->
/lib/libBrokenLocale.so.1

And when I launch ldconfig it is aborted:
# ./ldconfig -r /home/user/testroot/
Aborted

It shall be enough. It was three months ago, I am sorry again.
Comment 7 Ulrich Drepper 2010-05-03 21:05:38 UTC
OK, I fixed it now.  The patch differes from yours.
Comment 8 Vasily Gurevich 2010-05-25 19:24:24 UTC
Thanks a lot. Could you provide version number where can I verify it, please.
Thank you.
Comment 9 Petr Baudis 2010-05-25 19:28:07 UTC
Version 2.12 should have the fix, as well as 2.11.2.
Comment 10 Vasily Gurevich 2010-05-25 21:25:19 UTC
I checked glibc-2.11.2. It is still aborted. Will continue soon. Many thanks.
Comment 11 Vasily Gurevich 2010-05-27 19:28:51 UTC
Excuse me. It works fine. It was my mistake. Thanks a lot. Verified.
Comment 12 Petr Baudis 2010-06-01 00:29:08 UTC
Unfortunately, the aux_cache change introduced a bug - normally, ldconfig -r
never cares if the auxiliary cache is not available and that is not a fatal
problem, however this is not the case in case of ldconfig -r when executed as
non-root. In that case, ldconfig -r fails hard unless var/cache/ldconfig/ exists
within the chroot.

Patch to follow.
Comment 13 Petr Baudis 2010-06-01 00:29:44 UTC
Created attachment 4825 [details]
proposed patch
Comment 14 Ulrich Drepper 2011-05-30 01:47:23 UTC
This has long since been fixed.