On 08/22/2017 09:49 AM, Bruno Haible wrote:
It obviously does not handle the 'struct globnames' allocated with
the FLEXSIZEOF macro (lines 1719..1732).

Yes, and I installed the attached patch into Gnulib to try to fix this. I hope it is enough to pacify clang's Undefined Sanitizer. If not, Tim, please let us know, and thanks for reporting the problem.

I'll CC: this to Adhemerval Zanella, who's working on porting Gnulib glob.c back to glibc, as I worry that this patch (or something like it) should also be ported back.

>From 9211cb3147a1dac2a38a66fb02f24757b7350991 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 22 Aug 2017 10:13:50 -0700
Subject: [PATCH] glob: port to clang's Undefined Sanitizer
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Tim Rühsen in:
http://lists.gnu.org/archive/html/bug-gnulib/2017-08/msg00144.html
* lib/glob.c (FLEXIBLE_ARRAY_MEMBER) [_LIBC]: Define to empty.
(glob_in_dir): Do not rely on undefined behavior in accessing
struct members beyond their bounds.  Use a flexible array member
instead.
---
 ChangeLog  | 10 ++++++++++
 lib/glob.c | 29 +++++++++++++++--------------
 2 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 98643535f..3753cb2a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2017-08-22  Paul Eggert  <egg...@cs.ucla.edu>
+
+	glob: port to clang's Undefined Sanitizer
+	Problem reported by Tim Rühsen in:
+	http://lists.gnu.org/archive/html/bug-gnulib/2017-08/msg00144.html
+	* lib/glob.c (FLEXIBLE_ARRAY_MEMBER) [_LIBC]: Define to empty.
+	(glob_in_dir): Do not rely on undefined behavior in accessing
+	struct members beyond their bounds.  Use a flexible array member
+	instead.
+
 2017-08-21  Paul Eggert  <egg...@cs.ucla.edu>
 
 	vc-list-files: port to Solaris 10
diff --git a/lib/glob.c b/lib/glob.c
index d06101799..2a7cc8705 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -75,6 +75,7 @@
 #  define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
 # endif
 # define struct_stat64          struct stat64
+# define FLEXIBLE_ARRAY_MEMBER
 #else /* !_LIBC */
 # define __getlogin_r(buf, len) getlogin_r (buf, len)
 # define __stat64(fname, buf)   stat (fname, buf)
@@ -1590,25 +1591,25 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
 {
   size_t dirlen = strlen (directory);
   void *stream = NULL;
-  struct globnames
-    {
-      struct globnames *next;
-      size_t count;
-      char *name[64];
-    };
-  struct globnames init_names;
-  struct globnames *names = &init_names;
-  struct globnames *names_alloca = &init_names;
+# define GLOBNAMES_MEMBERS(nnames) \
+    struct globnames *next; size_t count; char *name[nnames];
+  struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) };
+  struct { GLOBNAMES_MEMBERS (64) } init_names_buf;
+  struct globnames *init_names = (struct globnames *) &init_names_buf;
+  struct globnames *names = init_names;
+  struct globnames *names_alloca = init_names;
   size_t nfound = 0;
   size_t cur = 0;
   int meta;
   int save;
   int result;
 
-  alloca_used += sizeof (init_names);
+  alloca_used += sizeof init_names_buf;
 
-  init_names.next = NULL;
-  init_names.count = sizeof init_names.name / sizeof init_names.name[0];
+  init_names->next = NULL;
+  init_names->count = ((sizeof init_names_buf
+                        - offsetof (struct globnames, name))
+                       / sizeof init_names->name[0]);
 
   meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE));
   if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
@@ -1789,7 +1790,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
                  and this is the block assigned to OLD here.  */
               if (names == NULL)
                 {
-                  assert (old == &init_names);
+                  assert (old == init_names);
                   break;
                 }
               cur = names->count;
@@ -1816,7 +1817,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
                  and this is the block assigned to OLD here.  */
               if (names == NULL)
                 {
-                  assert (old == &init_names);
+                  assert (old == init_names);
                   break;
                 }
               cur = names->count;
-- 
2.13.5

Reply via email to