d8307d
commit 0b79004569e5ce1669136b8c41564c3809730f15
d8307d
Author: Florian Weimer <fweimer@redhat.com>
d8307d
Date:   Tue Aug 28 12:57:46 2018 +0200
d8307d
d8307d
    regex: Add test tst-regcomp-truncated [BZ #23578]
d8307d
    
d8307d
    (cherry picked from commit 761404b74d9853ce1608195e24f25b78a910591a)
d8307d
d8307d
diff --git a/posix/Makefile b/posix/Makefile
d8307d
index 00c62841a282f15a..83162123f9c927a0 100644
d8307d
--- a/posix/Makefile
d8307d
+++ b/posix/Makefile
d8307d
@@ -96,7 +96,7 @@ tests		:= test-errno tstgetopt testfnm runtests runptests \
d8307d
 		   tst-posix_fadvise tst-posix_fadvise64 \
d8307d
 		   tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \
d8307d
 		   tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \
d8307d
-		   bug-regex38
d8307d
+		   bug-regex38 tst-regcomp-truncated
d8307d
 tests-internal	:= bug-regex5 bug-regex20 bug-regex33 \
d8307d
 		   tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
d8307d
 		   tst-glob_lstat_compat tst-spawn4-compat
d8307d
@@ -194,6 +194,7 @@ $(objpfx)tst-regex2.out: $(gen-locales)
d8307d
 $(objpfx)tst-regexloc.out: $(gen-locales)
d8307d
 $(objpfx)tst-rxspencer.out: $(gen-locales)
d8307d
 $(objpfx)tst-rxspencer-no-utf8.out: $(gen-locales)
d8307d
+$(objpfx)tst-regcomp-truncated.out: $(gen-locales)
d8307d
 endif
d8307d
 
d8307d
 # If we will use the generic uname implementation, we must figure out what
d8307d
diff --git a/posix/tst-regcomp-truncated.c b/posix/tst-regcomp-truncated.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..a4a1581bbc2b39eb
d8307d
--- /dev/null
d8307d
+++ b/posix/tst-regcomp-truncated.c
d8307d
@@ -0,0 +1,191 @@
d8307d
+/* Test compilation of truncated regular expressions.
d8307d
+   Copyright (C) 2018 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* This test constructs various patterns in an attempt to trigger
d8307d
+   over-reading the regular expression compiler, such as bug
d8307d
+   23578.  */
d8307d
+
d8307d
+#include <array_length.h>
d8307d
+#include <errno.h>
d8307d
+#include <locale.h>
d8307d
+#include <regex.h>
d8307d
+#include <stdio.h>
d8307d
+#include <stdlib.h>
d8307d
+#include <string.h>
d8307d
+#include <support/check.h>
d8307d
+#include <support/next_to_fault.h>
d8307d
+#include <support/support.h>
d8307d
+#include <support/test-driver.h>
d8307d
+#include <wchar.h>
d8307d
+
d8307d
+/* Locales to test.  */
d8307d
+static const char locales[][17] =
d8307d
+  {
d8307d
+    "C",
d8307d
+    "en_US.UTF-8",
d8307d
+    "de_DE.ISO-8859-1",
d8307d
+  };
d8307d
+
d8307d
+/* Syntax options.  Will be combined with other flags.  */
d8307d
+static const reg_syntax_t syntaxes[] =
d8307d
+  {
d8307d
+    RE_SYNTAX_EMACS,
d8307d
+    RE_SYNTAX_AWK,
d8307d
+    RE_SYNTAX_GNU_AWK,
d8307d
+    RE_SYNTAX_POSIX_AWK,
d8307d
+    RE_SYNTAX_GREP,
d8307d
+    RE_SYNTAX_EGREP,
d8307d
+    RE_SYNTAX_POSIX_EGREP,
d8307d
+    RE_SYNTAX_POSIX_BASIC,
d8307d
+    RE_SYNTAX_POSIX_EXTENDED,
d8307d
+    RE_SYNTAX_POSIX_MINIMAL_EXTENDED,
d8307d
+  };
d8307d
+
d8307d
+/* Trailing characters placed after the initial character.  */
d8307d
+static const char trailing_strings[][4] =
d8307d
+  {
d8307d
+    "",
d8307d
+    "[",
d8307d
+    "\\",
d8307d
+    "[\\",
d8307d
+    "(",
d8307d
+    "(\\",
d8307d
+    "\\(",
d8307d
+  };
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  /* Staging buffer for the constructed regular expression.  */
d8307d
+  char buffer[16];
d8307d
+
d8307d
+  /* Allocation used to detect over-reading by the regular expression
d8307d
+     compiler.  */
d8307d
+  struct support_next_to_fault ntf
d8307d
+    = support_next_to_fault_allocate (sizeof (buffer));
d8307d
+
d8307d
+  /* Arbitrary Unicode codepoint at which we stop generating
d8307d
+     characters.  We do not probe the whole range because that would
d8307d
+     take too long due to combinatorical exploision as the result of
d8307d
+     combination with other flags.  */
d8307d
+  static const wchar_t last_character = 0xfff;
d8307d
+
d8307d
+  for (size_t locale_idx = 0; locale_idx < array_length (locales);
d8307d
+       ++ locale_idx)
d8307d
+    {
d8307d
+      if (setlocale (LC_ALL, locales[locale_idx]) == NULL)
d8307d
+        {
d8307d
+          support_record_failure ();
d8307d
+          printf ("error: setlocale (\"%s\"): %m", locales[locale_idx]);
d8307d
+          continue;
d8307d
+        }
d8307d
+      if (test_verbose > 0)
d8307d
+        printf ("info: testing locale \"%s\"\n", locales[locale_idx]);
d8307d
+
d8307d
+      for (wchar_t wc = 0; wc <= last_character; ++wc)
d8307d
+        {
d8307d
+          char *after_wc;
d8307d
+          if (wc == 0)
d8307d
+            {
d8307d
+              /* wcrtomb treats L'\0' in a special way.  */
d8307d
+              *buffer = '\0';
d8307d
+              after_wc = &buffer[1];
d8307d
+            }
d8307d
+          else
d8307d
+            {
d8307d
+              mbstate_t ps = { };
d8307d
+              size_t ret = wcrtomb (buffer, wc, &ps);
d8307d
+              if (ret == (size_t) -1)
d8307d
+                {
d8307d
+                  /* EILSEQ means that the target character set
d8307d
+                     cannot encode the character.  */
d8307d
+                  if (errno != EILSEQ)
d8307d
+                    {
d8307d
+                      support_record_failure ();
d8307d
+                      printf ("error: wcrtomb (0x%x) failed: %m\n",
d8307d
+                              (unsigned) wc);
d8307d
+                    }
d8307d
+                  continue;
d8307d
+                }
d8307d
+              TEST_VERIFY_EXIT (ret != 0);
d8307d
+              after_wc = &buffer[ret];
d8307d
+            }
d8307d
+
d8307d
+          for (size_t trailing_idx = 0;
d8307d
+               trailing_idx < array_length (trailing_strings);
d8307d
+               ++trailing_idx)
d8307d
+            {
d8307d
+              char *after_trailing
d8307d
+                = stpcpy (after_wc, trailing_strings[trailing_idx]);
d8307d
+
d8307d
+              for (int do_nul = 0; do_nul < 2; ++do_nul)
d8307d
+                {
d8307d
+                  char *after_nul;
d8307d
+                  if (do_nul)
d8307d
+                    {
d8307d
+                      *after_trailing = '\0';
d8307d
+                      after_nul = &after_trailing[1];
d8307d
+                    }
d8307d
+                  else
d8307d
+                    after_nul = after_trailing;
d8307d
+
d8307d
+                  size_t length = after_nul - buffer;
d8307d
+
d8307d
+                  /* Make sure that the faulting region starts
d8307d
+                     after the used portion of the buffer.  */
d8307d
+                  char *ntf_start = ntf.buffer + sizeof (buffer) - length;
d8307d
+                  memcpy (ntf_start, buffer, length);
d8307d
+
d8307d
+                  for (const reg_syntax_t *psyntax = syntaxes;
d8307d
+                       psyntax < array_end (syntaxes); ++psyntax)
d8307d
+                    for (int do_icase = 0; do_icase < 2; ++do_icase)
d8307d
+                      {
d8307d
+                        re_syntax_options = *psyntax;
d8307d
+                        if (do_icase)
d8307d
+                          re_syntax_options |= RE_ICASE;
d8307d
+
d8307d
+                        regex_t reg;
d8307d
+                        memset (&reg, 0, sizeof (reg));
d8307d
+                        const char *msg = re_compile_pattern
d8307d
+                          (ntf_start, length, ®);
d8307d
+                        if (msg != NULL)
d8307d
+                          {
d8307d
+                            if (test_verbose > 0)
d8307d
+                              {
d8307d
+                                char *quoted = support_quote_blob
d8307d
+                                  (buffer, length);
d8307d
+                                printf ("info: compilation failed for pattern"
d8307d
+                                        " \"%s\", syntax 0x%lx: %s\n",
d8307d
+                                        quoted, re_syntax_options, msg);
d8307d
+                                free (quoted);
d8307d
+                              }
d8307d
+                          }
d8307d
+                        else
d8307d
+                          regfree (®);
d8307d
+                      }
d8307d
+                }
d8307d
+            }
d8307d
+        }
d8307d
+    }
d8307d
+
d8307d
+  support_next_to_fault_free (&ntf;;
d8307d
+
d8307d
+  return 0;
d8307d
+}
d8307d
+
d8307d
+#include <support/test-driver.c>