Subject: tests/df/skip-duplicates fails on Debian-kFreeBSD due to calling 'strstr(NULL,)'
From: Assaf Gordon <[email-masked]>
To: [email-masked]
Date: Thu, 26 Mar 2015 20:28:40 -0400
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

A somewhat exotic test failure:

On Debian/kFreeBSD 'tests/df/skip-duplicates' fails with 'df' segfaulting like so:

      ...
      ./tests/df/skip-duplicates.sh: line 113:  7741 Segmentation fault      LD_PRELOAD=./k.so df
      ...

The flow is:
1. the "k.so" file (inlined C code in 'tests/df/skip-duplicates.sh') returns 'struct mntent' in which '.mnt_opts' is NULL (not just empty string)
2. read_file_system_list() calls dev_from_mount_options(mnt->mnt_opts) .
3. in dev_from_mount_options()  "__linux__" is not defined (using FreeBSD kernel v9).
4. strstr() is called with 'mount_options==NULL'.
5. libc segfaults.

The direct culprit is 'dev_from_mount_options' in gnulib's mountlist.c, which has:

      static dev_t
      dev_from_mount_options (char const *mount_options)
      {
        /* GNU/Linux allows file system implementations to define their own
           meaning for "dev=" mount options, so don't trust the meaning
           here.  */
      # ifndef __linux__
        static char const dev_pattern[] = ",dev=";
        char const *devopt = strstr (mount_options, dev_pattern);
      ...

Using gdb, the stack-trace is:
      $ gdb ../src/df
      (gdb) set environment LD_PRELOAD=./k.so
      (gdb) start
      Program received signal SIGSEGV, Segmentation fault.
      0x0000000800abcc04 in strstr () from /lib/x86_64-kfreebsd-gnu/libc.so.0.1
      (gdb) bt
      #0  0x0000000800abcc04 in strstr () from /lib/x86_64-kfreebsd-gnu/libc.so.0.1
      #1  0x000000000040f7ed in dev_from_mount_options (mount_options=0x0) at lib/mountlist.c:363
      #2  0x000000000040fb6c in read_file_system_list (need_fs_type=false) at lib/mountlist.c:449
      #3  0x0000000000405751 in main (argc=1, argv=0x7fffffffd648) at src/df.c:1647

I'm not sure what is the correct,clean fix, attached are two options (one fixes the test, one avoids the call in lib/mountlist.c).

Regards,
   - assaf

P.S.
On FReeBSD-10.1 the test is skipped:
    skip-duplicates.sh: skipped test: $CC -shared ... failed to build a shared lib
    SKIP: tests/df/skip-duplicates.sh

--- tests/df/skip-duplicates.sh.bad	2014-07-13 18:09:52.000000000 -0400
+++ tests/df/skip-duplicates.sh	2015-03-26 20:24:22.000000000 -0400
@@ -56,13 +56,13 @@ struct mntent *getmntent (FILE *fp)
     }
 
   static struct mntent mntents[] = {
-    {.mnt_fsname="/short",  .mnt_dir="/invalid/mount/dir"},
-    {.mnt_fsname="fsname",  .mnt_dir="/",},
-    {.mnt_fsname="/fsname", .mnt_dir="/."},
-    {.mnt_fsname="/fsname", .mnt_dir="/"},
-    {.mnt_fsname="virtfs",  .mnt_dir="/NONROOT", .mnt_type="fstype1"},
-    {.mnt_fsname="virtfs2", .mnt_dir="/NONROOT", .mnt_type="fstype2"},
-    {.mnt_fsname="netns",   .mnt_dir="net:[1234567]"},
+    {.mnt_fsname="/short",  .mnt_dir="/invalid/mount/dir",            .mnt_opts=""},
+    {.mnt_fsname="fsname",  .mnt_dir="/",                             .mnt_opts=""},
+    {.mnt_fsname="/fsname", .mnt_dir="/.",                            .mnt_opts=""},
+    {.mnt_fsname="/fsname", .mnt_dir="/",                             .mnt_opts=""},
+    {.mnt_fsname="virtfs",  .mnt_dir="/NONROOT", .mnt_type="fstype1", .mnt_opts=""},
+    {.mnt_fsname="virtfs2", .mnt_dir="/NONROOT", .mnt_type="fstype2", .mnt_opts=""},
+    {.mnt_fsname="netns",   .mnt_dir="net:[1234567]",                 .mnt_opts=""},
   };
 
   if (done == 1)

--- lib/mountlist.c.bad	2015-03-26 20:30:05.000000000 -0400
+++ lib/mountlist.c	2015-03-26 20:36:54.000000000 -0400
@@ -360,7 +360,9 @@ dev_from_mount_options (char const *moun
 # ifndef __linux__
 
   static char const dev_pattern[] = ",dev=";
-  char const *devopt = strstr (mount_options, dev_pattern);
+  char const *devopt = (mount_options)?
+                       strstr (mount_options, dev_pattern):
+                       NULL;
 
   if (devopt)
     {