077c9d
commit 0ef2f4400c06927af34c515555f68840a70ba409
077c9d
Author: Wilco Dijkstra <wdijkstr@arm.com>
077c9d
Date:   Wed Sep 19 16:50:18 2018 +0100
077c9d
077c9d
    Fix strstr bug with huge needles (bug 23637)
077c9d
    
077c9d
    The generic strstr in GLIBC 2.28 fails to match huge needles.  The optimized
077c9d
    AVAILABLE macro reads ahead a large fixed amount to reduce the overhead of
077c9d
    repeatedly checking for the end of the string.  However if the needle length
077c9d
    is larger than this, two_way_long_needle may confuse this as meaning the end
077c9d
    of the string and return NULL.  This is fixed by adding the needle length to
077c9d
    the amount to read ahead.
077c9d
    
077c9d
            [BZ #23637]
077c9d
            * string/test-strstr.c (pr23637): New function.
077c9d
            (test_main): Add tests with longer needles.
077c9d
            * string/strcasestr.c (AVAILABLE): Fix readahead distance.
077c9d
            * string/strstr.c (AVAILABLE): Likewise.
077c9d
    
077c9d
        (cherry picked from commit 83a552b0bb9fc2a5e80a0ab3723c0a80ce1db9f2)
077c9d
077c9d
diff --git a/string/strcasestr.c b/string/strcasestr.c
077c9d
index 5909fe3cdba88e47..421764bd1b0ff22e 100644
077c9d
--- a/string/strcasestr.c
077c9d
+++ b/string/strcasestr.c
077c9d
@@ -37,8 +37,9 @@
077c9d
 /* Two-Way algorithm.  */
077c9d
 #define RETURN_TYPE char *
077c9d
 #define AVAILABLE(h, h_l, j, n_l)			\
077c9d
-  (((j) + (n_l) <= (h_l)) || ((h_l) += __strnlen ((void*)((h) + (h_l)), 512), \
077c9d
-			      (j) + (n_l) <= (h_l)))
077c9d
+  (((j) + (n_l) <= (h_l)) \
077c9d
+   || ((h_l) += __strnlen ((void*)((h) + (h_l)), (n_l) + 512), \
077c9d
+       (j) + (n_l) <= (h_l)))
077c9d
 #define CHECK_EOL (1)
077c9d
 #define RET0_IF_0(a) if (!a) goto ret0
077c9d
 #define CANON_ELEMENT(c) TOLOWER (c)
077c9d
diff --git a/string/strstr.c b/string/strstr.c
077c9d
index 265e9f310ce507ce..79ebcc75329d0b17 100644
077c9d
--- a/string/strstr.c
077c9d
+++ b/string/strstr.c
077c9d
@@ -33,8 +33,9 @@
077c9d
 
077c9d
 #define RETURN_TYPE char *
077c9d
 #define AVAILABLE(h, h_l, j, n_l)			\
077c9d
-  (((j) + (n_l) <= (h_l)) || ((h_l) += __strnlen ((void*)((h) + (h_l)), 512), \
077c9d
-			      (j) + (n_l) <= (h_l)))
077c9d
+  (((j) + (n_l) <= (h_l)) \
077c9d
+   || ((h_l) += __strnlen ((void*)((h) + (h_l)), (n_l) + 512), \
077c9d
+       (j) + (n_l) <= (h_l)))
077c9d
 #define CHECK_EOL (1)
077c9d
 #define RET0_IF_0(a) if (!a) goto ret0
077c9d
 #define FASTSEARCH(S,C,N) (void*) strchr ((void*)(S), (C))
077c9d
diff --git a/string/test-strstr.c b/string/test-strstr.c
077c9d
index 8d99716ff39cc2c2..5861b01b73e4c315 100644
077c9d
--- a/string/test-strstr.c
077c9d
+++ b/string/test-strstr.c
077c9d
@@ -151,6 +151,32 @@ check2 (void)
077c9d
     }
077c9d
 }
077c9d
 
077c9d
+#define N 1024
077c9d
+
077c9d
+static void
077c9d
+pr23637 (void)
077c9d
+{
077c9d
+  char *h = (char*) buf1;
077c9d
+  char *n = (char*) buf2;
077c9d
+
077c9d
+  for (int i = 0; i < N; i++)
077c9d
+    {
077c9d
+      n[i] = 'x';
077c9d
+      h[i] = ' ';
077c9d
+      h[i + N] = 'x';
077c9d
+    }
077c9d
+
077c9d
+  n[N] = '\0';
077c9d
+  h[N * 2] = '\0';
077c9d
+
077c9d
+  /* Ensure we don't match at the first 'x'.  */
077c9d
+  h[0] = 'x';
077c9d
+
077c9d
+  char *exp_result = stupid_strstr (h, n);
077c9d
+  FOR_EACH_IMPL (impl, 0)
077c9d
+    check_result (impl, h, n, exp_result);
077c9d
+}
077c9d
+
077c9d
 static int
077c9d
 test_main (void)
077c9d
 {
077c9d
@@ -158,6 +184,7 @@ test_main (void)
077c9d
 
077c9d
   check1 ();
077c9d
   check2 ();
077c9d
+  pr23637 ();
077c9d
 
077c9d
   printf ("%23s", "");
077c9d
   FOR_EACH_IMPL (impl, 0)
077c9d
@@ -202,6 +229,9 @@ test_main (void)
077c9d
 	do_test (15, 9, hlen, klen, 1);
077c9d
 	do_test (15, 15, hlen, klen, 0);
077c9d
 	do_test (15, 15, hlen, klen, 1);
077c9d
+
077c9d
+	do_test (15, 15, hlen + klen * 4, klen * 4, 0);
077c9d
+	do_test (15, 15, hlen + klen * 4, klen * 4, 1);
077c9d
       }
077c9d
 
077c9d
   do_test (0, 0, page_size - 1, 16, 0);