public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add strlcpy/wcslcpy testcase
@ 2023-06-30 20:45 Sunil K Pandey
  2023-07-15  1:07 ` DJ Delorie
  0 siblings, 1 reply; 12+ messages in thread
From: Sunil K Pandey @ 2023-06-30 20:45 UTC (permalink / raw)
  To: libc-alpha; +Cc: hjl.tools

This patch implements comprehensive tests for strlcpy/wcslcpy
functions.  Tests are mostly derived from strncpy test suites
and modified to incorporate strlcpy/wcslcpy specifications.
---
 string/Makefile       |   1 +
 string/test-strlcpy.c | 325 ++++++++++++++++++++++++++++++++++++++++++
 wcsmbs/Makefile       |   1 +
 wcsmbs/test-wcslcpy.c |  20 +++
 4 files changed, 347 insertions(+)
 create mode 100644 string/test-strlcpy.c
 create mode 100644 wcsmbs/test-wcslcpy.c

diff --git a/string/Makefile b/string/Makefile
index d3106d10a9..2bae2ba194 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -160,6 +160,7 @@ tests := \
   test-strcpy \
   test-strcspn \
   test-strdup \
+  test-strlcpy \
   test-strlen \
   test-strncasecmp \
   test-strncat \
diff --git a/string/test-strlcpy.c b/string/test-strlcpy.c
new file mode 100644
index 0000000000..43a721760f
--- /dev/null
+++ b/string/test-strlcpy.c
@@ -0,0 +1,325 @@
+/* Test strlcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifdef WIDE
+# include <wchar.h>
+# define CHAR wchar_t
+# define BIG_CHAR WCHAR_MAX
+# define SMALL_CHAR 1273
+# define MEMCMP wmemcmp
+# define MEMSET wmemset
+# define STRLEN wcslen
+#else
+# define CHAR char
+# define BIG_CHAR CHAR_MAX
+# define SMALL_CHAR 127
+# define MEMCMP memcmp
+# define MEMSET memset
+# define STRLEN strlen
+#endif /* !WIDE */
+
+
+#ifndef SIMPLE_STRLCPY
+# define TEST_MAIN
+# ifndef WIDE
+#  define TEST_NAME "strlcpy"
+# else
+#  define TEST_NAME "wcslcpy"
+# endif /* WIDE */
+# include "test-string.h"
+# ifndef WIDE
+#  define SIMPLE_STRLCPY simple_strlcpy
+#  define STRLCPY strlcpy
+# else
+#  define SIMPLE_STRLCPY simple_wcslcpy
+#  define STRLCPY wcslcpy
+# endif /* WIDE */
+
+
+IMPL (STRLCPY, 1)
+
+/* Naive implementation to verify results.  */
+size_t
+SIMPLE_STRLCPY (CHAR *dst, const CHAR *src, size_t n)
+{
+  size_t ret = STRLEN (src);
+  while (n--)
+    if ((*dst++ = *src++) == '\0')
+      return ret;
+  *dst = '\0';
+  return ret;
+}
+
+#endif /* !SIMPLE_STRLCPY */
+
+typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t);
+
+static void
+do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, size_t len, size_t n)
+{
+  if (CALL (impl, dst, src, n) != len)
+    {
+      error (0, 0, "Wrong result in function %s %zd %zd", impl->name,
+	     CALL (impl, dst, src, n), len);
+      ret = 1;
+      return;
+    }
+
+  if (n == 0)
+    return;
+
+  len = (len >= n ? n - 1 : len);
+  if (MEMCMP (dst, src, len) != 0)
+    {
+      error (0, 0, "Wrong result in function1 %s", impl->name);
+      ret = 1;
+      return;
+    }
+
+  if (dst [len] != '\0')
+    {
+      error (0, 0, "Wrong result in function2 %s", impl->name);
+      ret = 1;
+      return;
+    }
+}
+
+static void
+do_test (size_t align1, size_t align2, size_t len, size_t n, int max_char)
+{
+  size_t i;
+  CHAR *s1, *s2;
+
+  /* For wcslcpy: align1 and align2 here mean alignment not in bytes,
+     but in wchar_ts, in bytes it will equal to align * (sizeof (wchar_t)).  */
+  align1 &= 7;
+  if ((align1 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  align2 &= 7;
+  if ((align2 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  s1 = (CHAR *) (buf1) + align1;
+  s2 = (CHAR *) (buf2) + align2;
+
+  for (i = 0; i < len; ++i)
+    s1[i] = 32 + 23 * i % (max_char - 32);
+  s1[len] = 0;
+
+  FOR_EACH_IMPL (impl, 0)
+    do_one_test (impl, s2, s1, len, n);
+}
+
+static void
+do_page_tests (void)
+{
+  CHAR *s1, *s2;
+  const size_t maxoffset = 64;
+
+  /* Put s1 at the maxoffset from the edge of buf1's last page.  */
+  s1 = (CHAR *) buf1 + BUF1PAGES * page_size / sizeof(CHAR) - maxoffset;
+  /* s2 needs room to put a string with size of maxoffset + 1 at s2 +
+     (maxoffset - 1).  */
+  s2 = (CHAR *) buf2 + page_size / sizeof(CHAR) - maxoffset * 2;
+
+  MEMSET (s1, 'a', maxoffset - 1);
+  s1[maxoffset - 1] = '\0';
+
+  /* Both strings are bounded to a page with read/write access and the next
+     page is protected with PROT_NONE (meaning that any access outside of the
+     page regions will trigger an invalid memory access).
+
+     The loop copies the string s1 for all possible offsets up to maxoffset
+     for both inputs with a size larger than s1 (so memory access outside the
+     expected memory regions might trigger invalid access).  */
+
+  for (size_t off1 = 0; off1 < maxoffset; off1++)
+    {
+      for (size_t off2 = 0; off2 < maxoffset; off2++)
+	{
+	  FOR_EACH_IMPL (impl, 0)
+	    do_one_test (impl, s2 + off2, s1 + off1, maxoffset - off1 - 1,
+			 maxoffset + (maxoffset - off2));
+	}
+    }
+}
+
+static void
+do_random_tests (void)
+{
+  size_t i, j, n, align1, align2, len, size, mode;
+  CHAR *p1 = (CHAR *) (buf1 + page_size) - 1024;
+  CHAR *p2 = (CHAR *) (buf2 + page_size) - 1024;
+  size_t res;
+
+  for (n = 0; n < ITERATIONS; n++)
+    {
+      /* For wcslcpy: align1 and align2 here mean align not in bytes,
+	 but in wchar_ts, in bytes it will equal to align * (sizeof
+	 (wchar_t)).  */
+
+      mode = random ();
+      if (mode & 1)
+	{
+	  size = random () & 255;
+	  align1 = 512 - size - (random () & 15);
+	  if (mode & 2)
+	    align2 = align1 - (random () & 24);
+	  else
+	    align2 = align1 - (random () & 31);
+	  if (mode & 4)
+	    {
+	      j = align1;
+	      align1 = align2;
+	      align2 = j;
+	    }
+	  if (mode & 8)
+	    len = size - (random () & 31);
+	  else
+	    len = 512;
+	  if (len >= 512)
+	    len = random () & 511;
+	}
+      else
+	{
+	  align1 = random () & 31;
+	  if (mode & 2)
+	    align2 = random () & 31;
+	  else
+	    align2 = align1 + (random () & 24);
+	  len = random () & 511;
+	  j = align1;
+	  if (align2 > j)
+	    j = align2;
+	  if (mode & 4)
+	    {
+	      size = random () & 511;
+	      if (size + j > 512)
+		size = 512 - j - (random () & 31);
+	    }
+	  else
+	    size = 512 - j;
+	  if ((mode & 8) && len + j >= 512)
+	    len = 512 - j - (random () & 7);
+	}
+      j = len + align1;
+      for (i = 0; i < j; i++)
+	{
+	  p1[i] = random () & BIG_CHAR;
+	  if (i >= align1 && i < len + align1 && !p1[i])
+	    p1[i] = (random () & SMALL_CHAR) + 3;
+	}
+      p1[i] = 0;
+
+      FOR_EACH_IMPL (impl, 1)
+	{
+	  MEMSET (p2 - 64, '\1', 512 + 64);
+	  res = CALL (impl, (CHAR *) (p2 + align2),
+		      (CHAR *) (p1 + align1), size);
+	  if (res != len)
+	    {
+	      error (0, 0, "Iteration %zd - wrong result in function %s (%zd, %zd) %zd != %zd",
+		     n, impl->name, align1, align2, len, res);
+	      ret = 1;
+	    }
+	  for (j = 0; j < align2 + 64; ++j)
+	    {
+	      if (p2[j - 64] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage before, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = align2 + len + 1;
+	  if (size + align2 > j)
+	    j = size + align2;
+	  for (; j < 512; ++j)
+	    {
+	      if (p2[j] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage after, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = len;
+	  /* Check for zero size.  */
+	  if (size)
+	    {
+	      if (size <= j)
+		j = size - 1;
+	      if (MEMCMP (p1 + align1, p2 + align2, j))
+		{
+		  error (0, 0, "Iteration %zd - different strings, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		}
+	      if (p2[align2 + j])
+		{
+		  error (0, 0, "Iteration %zd - garbage after size, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	}
+    }
+}
+
+int
+test_main (void)
+{
+  size_t i;
+
+  test_init ();
+
+  printf ("%28s", "");
+  FOR_EACH_IMPL (impl, 0)
+    printf ("\t%s", impl->name);
+  putchar ('\n');
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (i, i, 16, 16, SMALL_CHAR);
+      do_test (i, i, 16, 16, BIG_CHAR);
+      do_test (i, 2 * i, 16, 16, SMALL_CHAR);
+      do_test (2 * i, i, 16, 16, BIG_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, SMALL_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, BIG_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, BIG_CHAR);
+    }
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (0, 0, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (0, 0, 16 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 16 << i, 8 << i, SMALL_CHAR);
+    }
+
+  do_random_tests ();
+  do_page_tests ();
+  return ret;
+}
+
+#include <support/test-driver.c>
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index 22192985e1..3fde8c76db 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -134,6 +134,7 @@ tests := \
   test-wcscpy \
   test-wcscspn \
   test-wcsdup \
+  test-wcslcpy \
   test-wcslen \
   test-wcsncat \
   test-wcsncmp \
diff --git a/wcsmbs/test-wcslcpy.c b/wcsmbs/test-wcslcpy.c
new file mode 100644
index 0000000000..91af5ae511
--- /dev/null
+++ b/wcsmbs/test-wcslcpy.c
@@ -0,0 +1,20 @@
+/* Test wcslcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define WIDE 1
+#include "../string/test-strlcpy.c"
-- 
2.38.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-06-30 20:45 [PATCH] Add strlcpy/wcslcpy testcase Sunil K Pandey
@ 2023-07-15  1:07 ` DJ Delorie
  2023-07-17 18:50   ` Carlos O'Donell
  0 siblings, 1 reply; 12+ messages in thread
From: DJ Delorie @ 2023-07-15  1:07 UTC (permalink / raw)
  To: Sunil K Pandey; +Cc: libc-alpha, hjl.tools

Sunil K Pandey via Libc-alpha <libc-alpha@sourceware.org> writes:
> This patch implements comprehensive tests for strlcpy/wcslcpy
> functions.  Tests are mostly derived from strncpy test suites
> and modified to incorporate strlcpy/wcslcpy specifications.


Looks OK to me, but the SIMPLE_STRLCPY implementation is incorrect.
However, it's also unused.  What is its purpose?

> +  test-strlcpy \

Ok.

> +/* Test strlcpy functions.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */

Ok.

> +#ifdef WIDE
> +# include <wchar.h>
> +# define CHAR wchar_t
> +# define BIG_CHAR WCHAR_MAX
> +# define SMALL_CHAR 1273
> +# define MEMCMP wmemcmp
> +# define MEMSET wmemset
> +# define STRLEN wcslen
> +#else
> +# define CHAR char
> +# define BIG_CHAR CHAR_MAX
> +# define SMALL_CHAR 127
> +# define MEMCMP memcmp
> +# define MEMSET memset
> +# define STRLEN strlen
> +#endif /* !WIDE */
> +
> +
> +#ifndef SIMPLE_STRLCPY
> +# define TEST_MAIN
> +# ifndef WIDE
> +#  define TEST_NAME "strlcpy"
> +# else
> +#  define TEST_NAME "wcslcpy"
> +# endif /* WIDE */
> +# include "test-string.h"
> +# ifndef WIDE
> +#  define SIMPLE_STRLCPY simple_strlcpy
> +#  define STRLCPY strlcpy
> +# else
> +#  define SIMPLE_STRLCPY simple_wcslcpy
> +#  define STRLCPY wcslcpy
> +# endif /* WIDE */

Consistent with other tests, so OK.

> +IMPL (STRLCPY, 1)

Ok.

> +/* Naive implementation to verify results.  */
> +size_t
> +SIMPLE_STRLCPY (CHAR *dst, const CHAR *src, size_t n)
> +{
> +  size_t ret = STRLEN (src);
> +  while (n--)
> +    if ((*dst++ = *src++) == '\0')
> +      return ret;
> +  *dst = '\0';
> +  return ret;
> +}

This is wrong.  It writes N+1 bytes to DST when SRC is long enough to.

Also, I don't see this used anywhere, including digging through other
tests to see if maybe there's magic involved.  Or, I removed it from the
test and the test still passed, so... why?

> +#endif /* !SIMPLE_STRLCPY */
> +
> +typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t);
> +
> +static void
> +do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, size_t len, size_t n)
> +{
> +  if (CALL (impl, dst, src, n) != len)
> +    {
> +      error (0, 0, "Wrong result in function %s %zd %zd", impl->name,
> +	     CALL (impl, dst, src, n), len);
> +      ret = 1;
> +      return;
> +    }

Verifying we return the expected value.  Ok.


> +  if (n == 0)
> +    return;

No buffer, nothing further to check.  Ok.

> +  len = (len >= n ? n - 1 : len);
> +  if (MEMCMP (dst, src, len) != 0)
> +    {
> +      error (0, 0, "Wrong result in function1 %s", impl->name);
> +      ret = 1;
> +      return;
> +    }

LEN is strlen(src) (does not include NUL)
N is buffer size (includes NUL)

If LEN >= N the string won't fit; at most N-1 bytes match
if LEN < N the string will fit; at most LEN+1 bytes match (including the
NUL)

the memcmp is the lesser of these, OK.

> +  if (dst [len] != '\0')
> +    {
> +      error (0, 0, "Wrong result in function2 %s", impl->name);
> +      ret = 1;
> +      return;
> +    }
> +}

Ok.

> +static void
> +do_test (size_t align1, size_t align2, size_t len, size_t n, int max_char)
> +{
> +  size_t i;
> +  CHAR *s1, *s2;
> +
> +  /* For wcslcpy: align1 and align2 here mean alignment not in bytes,
> +     but in wchar_ts, in bytes it will equal to align * (sizeof (wchar_t)).  */
> +  align1 &= 7;
> +  if ((align1 + len) * sizeof (CHAR) >= page_size)
> +    return;
> +
> +  align2 &= 7;
> +  if ((align2 + len) * sizeof (CHAR) >= page_size)
> +    return;
> +
> +  s1 = (CHAR *) (buf1) + align1;
> +  s2 = (CHAR *) (buf2) + align2;
> +
> +  for (i = 0; i < len; ++i)
> +    s1[i] = 32 + 23 * i % (max_char - 32);
> +  s1[len] = 0;
> +
> +  FOR_EACH_IMPL (impl, 0)
> +    do_one_test (impl, s2, s1, len, n);
> +}

Ok.

> +static void
> +do_page_tests (void)
> +{
> +  CHAR *s1, *s2;
> +  const size_t maxoffset = 64;
> +
> +  /* Put s1 at the maxoffset from the edge of buf1's last page.  */
> +  s1 = (CHAR *) buf1 + BUF1PAGES * page_size / sizeof(CHAR) - maxoffset;
> +  /* s2 needs room to put a string with size of maxoffset + 1 at s2 +
> +     (maxoffset - 1).  */
> +  s2 = (CHAR *) buf2 + page_size / sizeof(CHAR) - maxoffset * 2;
> +
> +  MEMSET (s1, 'a', maxoffset - 1);
> +  s1[maxoffset - 1] = '\0';
> +
> +  /* Both strings are bounded to a page with read/write access and the next
> +     page is protected with PROT_NONE (meaning that any access outside of the
> +     page regions will trigger an invalid memory access).
> +
> +     The loop copies the string s1 for all possible offsets up to maxoffset
> +     for both inputs with a size larger than s1 (so memory access outside the
> +     expected memory regions might trigger invalid access).  */
> +
> +  for (size_t off1 = 0; off1 < maxoffset; off1++)
> +    {
> +      for (size_t off2 = 0; off2 < maxoffset; off2++)
> +	{
> +	  FOR_EACH_IMPL (impl, 0)
> +	    do_one_test (impl, s2 + off2, s1 + off1, maxoffset - off1 - 1,
> +			 maxoffset + (maxoffset - off2));
> +	}
> +    }
> +}

Ok.

> +static void
> +do_random_tests (void)
> +{
> +  size_t i, j, n, align1, align2, len, size, mode;
> +  CHAR *p1 = (CHAR *) (buf1 + page_size) - 1024;
> +  CHAR *p2 = (CHAR *) (buf2 + page_size) - 1024;
> +  size_t res;
> +
> +  for (n = 0; n < ITERATIONS; n++)
> +    {
> +      /* For wcslcpy: align1 and align2 here mean align not in bytes,
> +	 but in wchar_ts, in bytes it will equal to align * (sizeof
> +	 (wchar_t)).  */
> +
> +      mode = random ();

Ok.

> +      if (mode & 1)
> +	{
> +	  size = random () & 255;
> +	  align1 = 512 - size - (random () & 15);
> +	  if (mode & 2)
> +	    align2 = align1 - (random () & 24);
> +	  else
> +	    align2 = align1 - (random () & 31);
> +	  if (mode & 4)
> +	    {
> +	      j = align1;
> +	      align1 = align2;
> +	      align2 = j;
> +	    }
> +	  if (mode & 8)
> +	    len = size - (random () & 31);
> +	  else
> +	    len = 512;
> +	  if (len >= 512)
> +	    len = random () & 511;
> +	}

Ok.

> +      else
> +	{
> +	  align1 = random () & 31;
> +	  if (mode & 2)
> +	    align2 = random () & 31;
> +	  else
> +	    align2 = align1 + (random () & 24);
> +	  len = random () & 511;
> +	  j = align1;
> +	  if (align2 > j)
> +	    j = align2;
> +	  if (mode & 4)
> +	    {
> +	      size = random () & 511;
> +	      if (size + j > 512)
> +		size = 512 - j - (random () & 31);
> +	    }
> +	  else
> +	    size = 512 - j;
> +	  if ((mode & 8) && len + j >= 512)
> +	    len = 512 - j - (random () & 7);
> +	}

end of mode 1

> +      j = len + align1;
> +      for (i = 0; i < j; i++)
> +	{
> +	  p1[i] = random () & BIG_CHAR;
> +	  if (i >= align1 && i < len + align1 && !p1[i])
> +	    p1[i] = (random () & SMALL_CHAR) + 3;
> +	}
> +      p1[i] = 0;

Ok.  string is now j (len+align1) chars long.

> +      FOR_EACH_IMPL (impl, 1)
> +	{
> +	  MEMSET (p2 - 64, '\1', 512 + 64);
> +	  res = CALL (impl, (CHAR *) (p2 + align2),
> +		      (CHAR *) (p1 + align1), size);

p1+align1 length would be j - align1 or len, ok.

> +	  if (res != len)

Ok.

> +	    {
> +	      error (0, 0, "Iteration %zd - wrong result in function %s (%zd, %zd) %zd != %zd",
> +		     n, impl->name, align1, align2, len, res);
> +	      ret = 1;
> +	    }

Ok.

> +	  for (j = 0; j < align2 + 64; ++j)
> +	    {
> +	      if (p2[j - 64] != '\1')
> +		{
> +		  error (0, 0, "Iteration %zd - garbage before, %s (%zd, %zd, %zd)",
> +			 n, impl->name, align1, align2, len);
> +		  ret = 1;
> +		  break;
> +		}
> +	    }

Ok.  Ends at p2[align2-1]

> +	  j = align2 + len + 1;
> +	  if (size + align2 > j)
> +	    j = size + align2;
> +	  for (; j < 512; ++j)
> +	    {
> +	      if (p2[j] != '\1')
> +		{
> +		  error (0, 0, "Iteration %zd - garbage after, %s (%zd, %zd, %zd)",
> +			 n, impl->name, align1, align2, len);
> +		  ret = 1;
> +		  break;
> +		}
> +	    }

Ok.

> +	  j = len;
> +	  /* Check for zero size.  */
> +	  if (size)
> +	    {
> +	      if (size <= j)
> +		j = size - 1;
> +	      if (MEMCMP (p1 + align1, p2 + align2, j))
> +		{
> +		  error (0, 0, "Iteration %zd - different strings, %s (%zd, %zd, %zd)",
> +			 n, impl->name, align1, align2, len);
> +		  ret = 1;
> +		}
> +	      if (p2[align2 + j])
> +		{
> +		  error (0, 0, "Iteration %zd - garbage after size, %s (%zd, %zd, %zd)",
> +			 n, impl->name, align1, align2, len);
> +		  ret = 1;
> +		  break;
> +		}
> +	    }
> +	}
> +    }
> +}

Ok.

> +int
> +test_main (void)
> +{
> +  size_t i;
> +
> +  test_init ();
> +
> +  printf ("%28s", "");
> +  FOR_EACH_IMPL (impl, 0)
> +    printf ("\t%s", impl->name);
> +  putchar ('\n');
> +
> +  for (i = 1; i < 8; ++i)
> +    {
> +      do_test (i, i, 16, 16, SMALL_CHAR);
> +      do_test (i, i, 16, 16, BIG_CHAR);
> +      do_test (i, 2 * i, 16, 16, SMALL_CHAR);
> +      do_test (2 * i, i, 16, 16, BIG_CHAR);
> +      do_test (8 - i, 2 * i, 1 << i, 2 << i, SMALL_CHAR);
> +      do_test (2 * i, 8 - i, 2 << i, 1 << i, SMALL_CHAR);
> +      do_test (8 - i, 2 * i, 1 << i, 2 << i, BIG_CHAR);
> +      do_test (2 * i, 8 - i, 2 << i, 1 << i, BIG_CHAR);
> +    }
> +
> +  for (i = 1; i < 8; ++i)
> +    {
> +      do_test (0, 0, 4 << i, 8 << i, SMALL_CHAR);
> +      do_test (0, 0, 16 << i, 8 << i, SMALL_CHAR);
> +      do_test (8 - i, 2 * i, 4 << i, 8 << i, SMALL_CHAR);
> +      do_test (8 - i, 2 * i, 16 << i, 8 << i, SMALL_CHAR);
> +    }
> +
> +  do_random_tests ();
> +  do_page_tests ();
> +  return ret;
> +}

Ok.

> +#include <support/test-driver.c>
> diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
> index 22192985e1..3fde8c76db 100644
> --- a/wcsmbs/Makefile
> +++ b/wcsmbs/Makefile
> @@ -134,6 +134,7 @@ tests := \
>    test-wcscpy \
>    test-wcscspn \
>    test-wcsdup \
> +  test-wcslcpy \
>    test-wcslen \
>    test-wcsncat \
>    test-wcsncmp \

Ok.

> diff --git a/wcsmbs/test-wcslcpy.c b/wcsmbs/test-wcslcpy.c
> new file mode 100644
> index 0000000000..91af5ae511
> --- /dev/null
> +++ b/wcsmbs/test-wcslcpy.c
> @@ -0,0 +1,20 @@
> +/* Test wcslcpy functions.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#define WIDE 1
> +#include "../string/test-strlcpy.c"

Ok.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-15  1:07 ` DJ Delorie
@ 2023-07-17 18:50   ` Carlos O'Donell
  2023-07-17 20:48     ` DJ Delorie
  0 siblings, 1 reply; 12+ messages in thread
From: Carlos O'Donell @ 2023-07-17 18:50 UTC (permalink / raw)
  To: DJ Delorie, Sunil K Pandey; +Cc: libc-alpha, hjl.tools

On 7/14/23 21:07, DJ Delorie via Libc-alpha wrote:
> Sunil K Pandey via Libc-alpha <libc-alpha@sourceware.org> writes:
>> This patch implements comprehensive tests for strlcpy/wcslcpy
>> functions.  Tests are mostly derived from strncpy test suites
>> and modified to incorporate strlcpy/wcslcpy specifications.
> 
> 
> Looks OK to me, but the SIMPLE_STRLCPY implementation is incorrect.
> However, it's also unused.  What is its purpose?
> 
>> +  test-strlcpy \
> 

We should avoid test-strlcpy and tst-strlcpy, and instead rename this to
tst-strclpy2, or some other name.

We currently have test-* and tst-* overlap for in string/* it is for
strlen.

Please update with a v2 given DJ's comments. Test cases like this can be
included without problem in the release (unless they fail).

> 
>> +/* Test strlcpy functions.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
> 
> Ok.
> 
>> +#ifdef WIDE
>> +# include <wchar.h>
>> +# define CHAR wchar_t
>> +# define BIG_CHAR WCHAR_MAX
>> +# define SMALL_CHAR 1273
>> +# define MEMCMP wmemcmp
>> +# define MEMSET wmemset
>> +# define STRLEN wcslen
>> +#else
>> +# define CHAR char
>> +# define BIG_CHAR CHAR_MAX
>> +# define SMALL_CHAR 127
>> +# define MEMCMP memcmp
>> +# define MEMSET memset
>> +# define STRLEN strlen
>> +#endif /* !WIDE */
>> +
>> +
>> +#ifndef SIMPLE_STRLCPY
>> +# define TEST_MAIN
>> +# ifndef WIDE
>> +#  define TEST_NAME "strlcpy"
>> +# else
>> +#  define TEST_NAME "wcslcpy"
>> +# endif /* WIDE */
>> +# include "test-string.h"
>> +# ifndef WIDE
>> +#  define SIMPLE_STRLCPY simple_strlcpy
>> +#  define STRLCPY strlcpy
>> +# else
>> +#  define SIMPLE_STRLCPY simple_wcslcpy
>> +#  define STRLCPY wcslcpy
>> +# endif /* WIDE */
> 
> Consistent with other tests, so OK.
> 
>> +IMPL (STRLCPY, 1)
> 
> Ok.
> 
>> +/* Naive implementation to verify results.  */
>> +size_t
>> +SIMPLE_STRLCPY (CHAR *dst, const CHAR *src, size_t n)
>> +{
>> +  size_t ret = STRLEN (src);
>> +  while (n--)
>> +    if ((*dst++ = *src++) == '\0')
>> +      return ret;
>> +  *dst = '\0';
>> +  return ret;
>> +}
> 
> This is wrong.  It writes N+1 bytes to DST when SRC is long enough to.
> 
> Also, I don't see this used anywhere, including digging through other
> tests to see if maybe there's magic involved.  Or, I removed it from the
> test and the test still passed, so... why?
> 
>> +#endif /* !SIMPLE_STRLCPY */
>> +
>> +typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t);
>> +
>> +static void
>> +do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, size_t len, size_t n)
>> +{
>> +  if (CALL (impl, dst, src, n) != len)
>> +    {
>> +      error (0, 0, "Wrong result in function %s %zd %zd", impl->name,
>> +	     CALL (impl, dst, src, n), len);
>> +      ret = 1;
>> +      return;
>> +    }
> 
> Verifying we return the expected value.  Ok.
> 
> 
>> +  if (n == 0)
>> +    return;
> 
> No buffer, nothing further to check.  Ok.
> 
>> +  len = (len >= n ? n - 1 : len);
>> +  if (MEMCMP (dst, src, len) != 0)
>> +    {
>> +      error (0, 0, "Wrong result in function1 %s", impl->name);
>> +      ret = 1;
>> +      return;
>> +    }
> 
> LEN is strlen(src) (does not include NUL)
> N is buffer size (includes NUL)
> 
> If LEN >= N the string won't fit; at most N-1 bytes match
> if LEN < N the string will fit; at most LEN+1 bytes match (including the
> NUL)
> 
> the memcmp is the lesser of these, OK.
> 
>> +  if (dst [len] != '\0')
>> +    {
>> +      error (0, 0, "Wrong result in function2 %s", impl->name);
>> +      ret = 1;
>> +      return;
>> +    }
>> +}
> 
> Ok.
> 
>> +static void
>> +do_test (size_t align1, size_t align2, size_t len, size_t n, int max_char)
>> +{
>> +  size_t i;
>> +  CHAR *s1, *s2;
>> +
>> +  /* For wcslcpy: align1 and align2 here mean alignment not in bytes,
>> +     but in wchar_ts, in bytes it will equal to align * (sizeof (wchar_t)).  */
>> +  align1 &= 7;
>> +  if ((align1 + len) * sizeof (CHAR) >= page_size)
>> +    return;
>> +
>> +  align2 &= 7;
>> +  if ((align2 + len) * sizeof (CHAR) >= page_size)
>> +    return;
>> +
>> +  s1 = (CHAR *) (buf1) + align1;
>> +  s2 = (CHAR *) (buf2) + align2;
>> +
>> +  for (i = 0; i < len; ++i)
>> +    s1[i] = 32 + 23 * i % (max_char - 32);
>> +  s1[len] = 0;
>> +
>> +  FOR_EACH_IMPL (impl, 0)
>> +    do_one_test (impl, s2, s1, len, n);
>> +}
> 
> Ok.
> 
>> +static void
>> +do_page_tests (void)
>> +{
>> +  CHAR *s1, *s2;
>> +  const size_t maxoffset = 64;
>> +
>> +  /* Put s1 at the maxoffset from the edge of buf1's last page.  */
>> +  s1 = (CHAR *) buf1 + BUF1PAGES * page_size / sizeof(CHAR) - maxoffset;
>> +  /* s2 needs room to put a string with size of maxoffset + 1 at s2 +
>> +     (maxoffset - 1).  */
>> +  s2 = (CHAR *) buf2 + page_size / sizeof(CHAR) - maxoffset * 2;
>> +
>> +  MEMSET (s1, 'a', maxoffset - 1);
>> +  s1[maxoffset - 1] = '\0';
>> +
>> +  /* Both strings are bounded to a page with read/write access and the next
>> +     page is protected with PROT_NONE (meaning that any access outside of the
>> +     page regions will trigger an invalid memory access).
>> +
>> +     The loop copies the string s1 for all possible offsets up to maxoffset
>> +     for both inputs with a size larger than s1 (so memory access outside the
>> +     expected memory regions might trigger invalid access).  */
>> +
>> +  for (size_t off1 = 0; off1 < maxoffset; off1++)
>> +    {
>> +      for (size_t off2 = 0; off2 < maxoffset; off2++)
>> +	{
>> +	  FOR_EACH_IMPL (impl, 0)
>> +	    do_one_test (impl, s2 + off2, s1 + off1, maxoffset - off1 - 1,
>> +			 maxoffset + (maxoffset - off2));
>> +	}
>> +    }
>> +}
> 
> Ok.
> 
>> +static void
>> +do_random_tests (void)
>> +{
>> +  size_t i, j, n, align1, align2, len, size, mode;
>> +  CHAR *p1 = (CHAR *) (buf1 + page_size) - 1024;
>> +  CHAR *p2 = (CHAR *) (buf2 + page_size) - 1024;
>> +  size_t res;
>> +
>> +  for (n = 0; n < ITERATIONS; n++)
>> +    {
>> +      /* For wcslcpy: align1 and align2 here mean align not in bytes,
>> +	 but in wchar_ts, in bytes it will equal to align * (sizeof
>> +	 (wchar_t)).  */
>> +
>> +      mode = random ();
> 
> Ok.
> 
>> +      if (mode & 1)
>> +	{
>> +	  size = random () & 255;
>> +	  align1 = 512 - size - (random () & 15);
>> +	  if (mode & 2)
>> +	    align2 = align1 - (random () & 24);
>> +	  else
>> +	    align2 = align1 - (random () & 31);
>> +	  if (mode & 4)
>> +	    {
>> +	      j = align1;
>> +	      align1 = align2;
>> +	      align2 = j;
>> +	    }
>> +	  if (mode & 8)
>> +	    len = size - (random () & 31);
>> +	  else
>> +	    len = 512;
>> +	  if (len >= 512)
>> +	    len = random () & 511;
>> +	}
> 
> Ok.
> 
>> +      else
>> +	{
>> +	  align1 = random () & 31;
>> +	  if (mode & 2)
>> +	    align2 = random () & 31;
>> +	  else
>> +	    align2 = align1 + (random () & 24);
>> +	  len = random () & 511;
>> +	  j = align1;
>> +	  if (align2 > j)
>> +	    j = align2;
>> +	  if (mode & 4)
>> +	    {
>> +	      size = random () & 511;
>> +	      if (size + j > 512)
>> +		size = 512 - j - (random () & 31);
>> +	    }
>> +	  else
>> +	    size = 512 - j;
>> +	  if ((mode & 8) && len + j >= 512)
>> +	    len = 512 - j - (random () & 7);
>> +	}
> 
> end of mode 1
> 
>> +      j = len + align1;
>> +      for (i = 0; i < j; i++)
>> +	{
>> +	  p1[i] = random () & BIG_CHAR;
>> +	  if (i >= align1 && i < len + align1 && !p1[i])
>> +	    p1[i] = (random () & SMALL_CHAR) + 3;
>> +	}
>> +      p1[i] = 0;
> 
> Ok.  string is now j (len+align1) chars long.
> 
>> +      FOR_EACH_IMPL (impl, 1)
>> +	{
>> +	  MEMSET (p2 - 64, '\1', 512 + 64);
>> +	  res = CALL (impl, (CHAR *) (p2 + align2),
>> +		      (CHAR *) (p1 + align1), size);
> 
> p1+align1 length would be j - align1 or len, ok.
> 
>> +	  if (res != len)
> 
> Ok.
> 
>> +	    {
>> +	      error (0, 0, "Iteration %zd - wrong result in function %s (%zd, %zd) %zd != %zd",
>> +		     n, impl->name, align1, align2, len, res);
>> +	      ret = 1;
>> +	    }
> 
> Ok.
> 
>> +	  for (j = 0; j < align2 + 64; ++j)
>> +	    {
>> +	      if (p2[j - 64] != '\1')
>> +		{
>> +		  error (0, 0, "Iteration %zd - garbage before, %s (%zd, %zd, %zd)",
>> +			 n, impl->name, align1, align2, len);
>> +		  ret = 1;
>> +		  break;
>> +		}
>> +	    }
> 
> Ok.  Ends at p2[align2-1]
> 
>> +	  j = align2 + len + 1;
>> +	  if (size + align2 > j)
>> +	    j = size + align2;
>> +	  for (; j < 512; ++j)
>> +	    {
>> +	      if (p2[j] != '\1')
>> +		{
>> +		  error (0, 0, "Iteration %zd - garbage after, %s (%zd, %zd, %zd)",
>> +			 n, impl->name, align1, align2, len);
>> +		  ret = 1;
>> +		  break;
>> +		}
>> +	    }
> 
> Ok.
> 
>> +	  j = len;
>> +	  /* Check for zero size.  */
>> +	  if (size)
>> +	    {
>> +	      if (size <= j)
>> +		j = size - 1;
>> +	      if (MEMCMP (p1 + align1, p2 + align2, j))
>> +		{
>> +		  error (0, 0, "Iteration %zd - different strings, %s (%zd, %zd, %zd)",
>> +			 n, impl->name, align1, align2, len);
>> +		  ret = 1;
>> +		}
>> +	      if (p2[align2 + j])
>> +		{
>> +		  error (0, 0, "Iteration %zd - garbage after size, %s (%zd, %zd, %zd)",
>> +			 n, impl->name, align1, align2, len);
>> +		  ret = 1;
>> +		  break;
>> +		}
>> +	    }
>> +	}
>> +    }
>> +}
> 
> Ok.
> 
>> +int
>> +test_main (void)
>> +{
>> +  size_t i;
>> +
>> +  test_init ();
>> +
>> +  printf ("%28s", "");
>> +  FOR_EACH_IMPL (impl, 0)
>> +    printf ("\t%s", impl->name);
>> +  putchar ('\n');
>> +
>> +  for (i = 1; i < 8; ++i)
>> +    {
>> +      do_test (i, i, 16, 16, SMALL_CHAR);
>> +      do_test (i, i, 16, 16, BIG_CHAR);
>> +      do_test (i, 2 * i, 16, 16, SMALL_CHAR);
>> +      do_test (2 * i, i, 16, 16, BIG_CHAR);
>> +      do_test (8 - i, 2 * i, 1 << i, 2 << i, SMALL_CHAR);
>> +      do_test (2 * i, 8 - i, 2 << i, 1 << i, SMALL_CHAR);
>> +      do_test (8 - i, 2 * i, 1 << i, 2 << i, BIG_CHAR);
>> +      do_test (2 * i, 8 - i, 2 << i, 1 << i, BIG_CHAR);
>> +    }
>> +
>> +  for (i = 1; i < 8; ++i)
>> +    {
>> +      do_test (0, 0, 4 << i, 8 << i, SMALL_CHAR);
>> +      do_test (0, 0, 16 << i, 8 << i, SMALL_CHAR);
>> +      do_test (8 - i, 2 * i, 4 << i, 8 << i, SMALL_CHAR);
>> +      do_test (8 - i, 2 * i, 16 << i, 8 << i, SMALL_CHAR);
>> +    }
>> +
>> +  do_random_tests ();
>> +  do_page_tests ();
>> +  return ret;
>> +}
> 
> Ok.
> 
>> +#include <support/test-driver.c>
>> diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
>> index 22192985e1..3fde8c76db 100644
>> --- a/wcsmbs/Makefile
>> +++ b/wcsmbs/Makefile
>> @@ -134,6 +134,7 @@ tests := \
>>    test-wcscpy \
>>    test-wcscspn \
>>    test-wcsdup \
>> +  test-wcslcpy \
>>    test-wcslen \
>>    test-wcsncat \
>>    test-wcsncmp \
> 
> Ok.
> 
>> diff --git a/wcsmbs/test-wcslcpy.c b/wcsmbs/test-wcslcpy.c
>> new file mode 100644
>> index 0000000000..91af5ae511
>> --- /dev/null
>> +++ b/wcsmbs/test-wcslcpy.c
>> @@ -0,0 +1,20 @@
>> +/* Test wcslcpy functions.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#define WIDE 1
>> +#include "../string/test-strlcpy.c"
> 
> Ok.
> 

-- 
Cheers,
Carlos.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-17 18:50   ` Carlos O'Donell
@ 2023-07-17 20:48     ` DJ Delorie
  2023-07-27 17:51       ` [PATCH v2] " Sunil K Pandey
  2023-07-27 17:55       ` [PATCH] " Sunil Pandey
  0 siblings, 2 replies; 12+ messages in thread
From: DJ Delorie @ 2023-07-17 20:48 UTC (permalink / raw)
  To: Carlos O'Donell; +Cc: skpgkp2, libc-alpha, hjl.tools

"Carlos O'Donell" <carlos@redhat.com> writes:
> Please update with a v2 given DJ's comments. Test cases like this can be
> included without problem in the release (unless they fail).

Also, Carlos and I figured out what the SIMPLE_* function is for.  You
should add it as a second IMPL() so that your testsuite itself can be
validated.  Please consider this for v2 ;-)


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2] Add strlcpy/wcslcpy testcase
  2023-07-17 20:48     ` DJ Delorie
@ 2023-07-27 17:51       ` Sunil K Pandey
  2023-07-27 17:55       ` [PATCH] " Sunil Pandey
  1 sibling, 0 replies; 12+ messages in thread
From: Sunil K Pandey @ 2023-07-27 17:51 UTC (permalink / raw)
  To: libc-alpha; +Cc: hjl.tools

This patch implements comprehensive tests for strlcpy/wcslcpy
functions.  Tests are mostly derived from strncpy test suites
and modified to incorporate strlcpy/wcslcpy specifications.

Changes from v1:

Rename test-strlcpy to tst-strlcpy2.
Rename test-wcslcpy to tst-wcslcpy2.
Remove unused SIMPLE_STRLCPY function.
Rearrange defines in alphabetical order.
---
 string/Makefile       |   1 +
 string/tst-strlcpy2.c | 303 ++++++++++++++++++++++++++++++++++++++++++
 wcsmbs/Makefile       |   1 +
 wcsmbs/tst-wcslcpy2.c |  20 +++
 4 files changed, 325 insertions(+)
 create mode 100644 string/tst-strlcpy2.c
 create mode 100644 wcsmbs/tst-wcslcpy2.c

diff --git a/string/Makefile b/string/Makefile
index 8cdfd5b000..cecaa796a8 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -199,6 +199,7 @@ tests := \
   tst-strfry \
   tst-strlcat \
   tst-strlcpy \
+  tst-strlcpy2 \
   tst-strlen \
   tst-strtok \
   tst-strtok_r \
diff --git a/string/tst-strlcpy2.c b/string/tst-strlcpy2.c
new file mode 100644
index 0000000000..4d6ebb0ebd
--- /dev/null
+++ b/string/tst-strlcpy2.c
@@ -0,0 +1,303 @@
+/* Test strlcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define TEST_MAIN
+#ifndef WIDE
+# define TEST_NAME "strlcpy"
+#else
+# define TEST_NAME "wcslcpy"
+#endif /* WIDE */
+#include "test-string.h"
+
+#ifdef WIDE
+# include <wchar.h>
+# define BIG_CHAR WCHAR_MAX
+# define CHAR wchar_t
+# define MEMCMP wmemcmp
+# define MEMSET wmemset
+# define SMALL_CHAR 1273
+# define STRLCPY wcslcpy
+# define STRLEN wcslen
+#else
+# define BIG_CHAR CHAR_MAX
+# define CHAR char
+# define MEMCMP memcmp
+# define MEMSET memset
+# define SMALL_CHAR 127
+# define STRLCPY strlcpy
+# define STRLEN strlen
+#endif /* !WIDE */
+
+IMPL (STRLCPY, 1)
+
+typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t);
+
+static void
+do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, size_t len, size_t n)
+{
+  if (CALL (impl, dst, src, n) != len)
+    {
+      error (0, 0, "Wrong result in function %s %zd %zd", impl->name,
+	     CALL (impl, dst, src, n), len);
+      ret = 1;
+      return;
+    }
+
+  if (n == 0)
+    return;
+
+  len = (len >= n ? n - 1 : len);
+  if (MEMCMP (dst, src, len) != 0)
+    {
+      error (0, 0, "Wrong result in function1 %s", impl->name);
+      ret = 1;
+      return;
+    }
+
+  if (dst [len] != '\0')
+    {
+      error (0, 0, "Wrong result in function2 %s", impl->name);
+      ret = 1;
+      return;
+    }
+}
+
+static void
+do_test (size_t align1, size_t align2, size_t len, size_t n, int max_char)
+{
+  size_t i;
+  CHAR *s1, *s2;
+
+  /* For wcslcpy: align1 and align2 here mean alignment not in bytes,
+     but in wchar_ts, in bytes it will equal to align * (sizeof (wchar_t)).  */
+  align1 &= 7;
+  if ((align1 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  align2 &= 7;
+  if ((align2 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  s1 = (CHAR *) (buf1) + align1;
+  s2 = (CHAR *) (buf2) + align2;
+
+  for (i = 0; i < len; ++i)
+    s1[i] = 32 + 23 * i % (max_char - 32);
+  s1[len] = 0;
+
+  FOR_EACH_IMPL (impl, 0)
+    do_one_test (impl, s2, s1, len, n);
+}
+
+static void
+do_page_tests (void)
+{
+  CHAR *s1, *s2;
+  const size_t maxoffset = 64;
+
+  /* Put s1 at the maxoffset from the edge of buf1's last page.  */
+  s1 = (CHAR *) buf1 + BUF1PAGES * page_size / sizeof(CHAR) - maxoffset;
+  /* s2 needs room to put a string with size of maxoffset + 1 at s2 +
+     (maxoffset - 1).  */
+  s2 = (CHAR *) buf2 + page_size / sizeof(CHAR) - maxoffset * 2;
+
+  MEMSET (s1, 'a', maxoffset - 1);
+  s1[maxoffset - 1] = '\0';
+
+  /* Both strings are bounded to a page with read/write access and the next
+     page is protected with PROT_NONE (meaning that any access outside of the
+     page regions will trigger an invalid memory access).
+
+     The loop copies the string s1 for all possible offsets up to maxoffset
+     for both inputs with a size larger than s1 (so memory access outside the
+     expected memory regions might trigger invalid access).  */
+
+  for (size_t off1 = 0; off1 < maxoffset; off1++)
+    {
+      for (size_t off2 = 0; off2 < maxoffset; off2++)
+	{
+	  FOR_EACH_IMPL (impl, 0)
+	    do_one_test (impl, s2 + off2, s1 + off1, maxoffset - off1 - 1,
+			 maxoffset + (maxoffset - off2));
+	}
+    }
+}
+
+static void
+do_random_tests (void)
+{
+  size_t i, j, n, align1, align2, len, size, mode;
+  CHAR *p1 = (CHAR *) (buf1 + page_size) - 1024;
+  CHAR *p2 = (CHAR *) (buf2 + page_size) - 1024;
+  size_t res;
+
+  for (n = 0; n < ITERATIONS; n++)
+    {
+      /* For wcslcpy: align1 and align2 here mean align not in bytes,
+	 but in wchar_ts, in bytes it will equal to align * (sizeof
+	 (wchar_t)).  */
+
+      mode = random ();
+      if (mode & 1)
+	{
+	  size = random () & 255;
+	  align1 = 512 - size - (random () & 15);
+	  if (mode & 2)
+	    align2 = align1 - (random () & 24);
+	  else
+	    align2 = align1 - (random () & 31);
+	  if (mode & 4)
+	    {
+	      j = align1;
+	      align1 = align2;
+	      align2 = j;
+	    }
+	  if (mode & 8)
+	    len = size - (random () & 31);
+	  else
+	    len = 512;
+	  if (len >= 512)
+	    len = random () & 511;
+	}
+      else
+	{
+	  align1 = random () & 31;
+	  if (mode & 2)
+	    align2 = random () & 31;
+	  else
+	    align2 = align1 + (random () & 24);
+	  len = random () & 511;
+	  j = align1;
+	  if (align2 > j)
+	    j = align2;
+	  if (mode & 4)
+	    {
+	      size = random () & 511;
+	      if (size + j > 512)
+		size = 512 - j - (random () & 31);
+	    }
+	  else
+	    size = 512 - j;
+	  if ((mode & 8) && len + j >= 512)
+	    len = 512 - j - (random () & 7);
+	}
+      j = len + align1;
+      for (i = 0; i < j; i++)
+	{
+	  p1[i] = random () & BIG_CHAR;
+	  if (i >= align1 && i < len + align1 && !p1[i])
+	    p1[i] = (random () & SMALL_CHAR) + 3;
+	}
+      p1[i] = 0;
+
+      FOR_EACH_IMPL (impl, 1)
+	{
+	  MEMSET (p2 - 64, '\1', 512 + 64);
+	  res = CALL (impl, (CHAR *) (p2 + align2),
+		      (CHAR *) (p1 + align1), size);
+	  if (res != len)
+	    {
+	      error (0, 0, "Iteration %zd - wrong result in function %s (%zd, %zd) %zd != %zd",
+		     n, impl->name, align1, align2, len, res);
+	      ret = 1;
+	    }
+	  for (j = 0; j < align2 + 64; ++j)
+	    {
+	      if (p2[j - 64] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage before, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = align2 + len + 1;
+	  if (size + align2 > j)
+	    j = size + align2;
+	  for (; j < 512; ++j)
+	    {
+	      if (p2[j] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage after, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = len;
+	  /* Check for zero size.  */
+	  if (size)
+	    {
+	      if (size <= j)
+		j = size - 1;
+	      if (MEMCMP (p1 + align1, p2 + align2, j))
+		{
+		  error (0, 0, "Iteration %zd - different strings, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		}
+	      if (p2[align2 + j])
+		{
+		  error (0, 0, "Iteration %zd - garbage after size, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	}
+    }
+}
+
+int
+test_main (void)
+{
+  size_t i;
+
+  test_init ();
+
+  printf ("%28s", "");
+  FOR_EACH_IMPL (impl, 0)
+    printf ("\t%s", impl->name);
+  putchar ('\n');
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (i, i, 16, 16, SMALL_CHAR);
+      do_test (i, i, 16, 16, BIG_CHAR);
+      do_test (i, 2 * i, 16, 16, SMALL_CHAR);
+      do_test (2 * i, i, 16, 16, BIG_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, SMALL_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, BIG_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, BIG_CHAR);
+    }
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (0, 0, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (0, 0, 16 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 16 << i, 8 << i, SMALL_CHAR);
+    }
+
+  do_random_tests ();
+  do_page_tests ();
+  return ret;
+}
+
+#include <support/test-driver.c>
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index 431136b9c9..d7626b386d 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -180,6 +180,7 @@ tests := \
   tst-wcrtomb \
   tst-wcslcat \
   tst-wcslcpy \
+  tst-wcslcpy2 \
   tst-wcsnlen \
   tst-wcstod-nan-locale \
   tst-wcstod-nan-sign \
diff --git a/wcsmbs/tst-wcslcpy2.c b/wcsmbs/tst-wcslcpy2.c
new file mode 100644
index 0000000000..bec40ed135
--- /dev/null
+++ b/wcsmbs/tst-wcslcpy2.c
@@ -0,0 +1,20 @@
+/* Test wcslcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define WIDE 1
+#include "../string/tst-strlcpy2.c"
-- 
2.41.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-17 20:48     ` DJ Delorie
  2023-07-27 17:51       ` [PATCH v2] " Sunil K Pandey
@ 2023-07-27 17:55       ` Sunil Pandey
  2023-07-27 18:50         ` DJ Delorie
  1 sibling, 1 reply; 12+ messages in thread
From: Sunil Pandey @ 2023-07-27 17:55 UTC (permalink / raw)
  To: DJ Delorie; +Cc: Carlos O'Donell, libc-alpha, hjl.tools

[-- Attachment #1: Type: text/plain, Size: 616 bytes --]

On Mon, Jul 17, 2023 at 1:48 PM DJ Delorie <dj@redhat.com> wrote:

> "Carlos O'Donell" <carlos@redhat.com> writes:
> > Please update with a v2 given DJ's comments. Test cases like this can be
> > included without problem in the release (unless they fail).
>
> Also, Carlos and I figured out what the SIMPLE_* function is for.  You
> should add it as a second IMPL() so that your testsuite itself can be
> validated.  Please consider this for v2 ;-)
>
>
Thank you so much for the review, just submitted v2.
Removed unused SIMPLE_* function as functionality is already checked in the
test case itself.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-27 17:55       ` [PATCH] " Sunil Pandey
@ 2023-07-27 18:50         ` DJ Delorie
  2023-07-27 19:10           ` Sunil Pandey
  0 siblings, 1 reply; 12+ messages in thread
From: DJ Delorie @ 2023-07-27 18:50 UTC (permalink / raw)
  To: Sunil Pandey; +Cc: libc-alpha

Sunil Pandey <skpgkp2@gmail.com> writes:
> Removed unused SIMPLE_* function as functionality is already checked
> in the test case itself.

Perhaps I wasn't clear in my comments on this.  The SIMPLE function
isn't there to test basic functionality of the function under test, the
SIMPLE function is there to test *your testsuite*.

If the test ever fails on the SIMPLE function, you know the test is
wrong.

If the test passes the SIMPLE function but fails the library function,
you know the library function is wrong.

Without the SIMPLE function, if the test fails, you don't know if it's
the test or the library function that needs fixing.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-27 18:50         ` DJ Delorie
@ 2023-07-27 19:10           ` Sunil Pandey
  2023-07-27 19:20             ` DJ Delorie
  0 siblings, 1 reply; 12+ messages in thread
From: Sunil Pandey @ 2023-07-27 19:10 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

[-- Attachment #1: Type: text/plain, Size: 1725 bytes --]

On Thu, Jul 27, 2023 at 11:50 AM DJ Delorie <dj@redhat.com> wrote:

> Sunil Pandey <skpgkp2@gmail.com> writes:
> > Removed unused SIMPLE_* function as functionality is already checked
> > in the test case itself.
>
> Perhaps I wasn't clear in my comments on this.  The SIMPLE function
> isn't there to test basic functionality of the function under test, the
> SIMPLE function is there to test *your testsuite*.
>
> If the test ever fails on the SIMPLE function, you know the test is
> wrong.
>
> If the test passes the SIMPLE function but fails the library function,
> you know the library function is wrong.
>
> Without the SIMPLE function, if the test fails, you don't know if it's
> the test or the library function that needs fixing.
>

Actually, it's already testing the generic version of strlcpy
implementation from the string/strlcpy.c
file, so no need to implement another SIMPLE_* function.

$ gdb string/tst-strlcpy2
GNU gdb (GDB) Fedora Linux 13.2-2.fc37
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html
>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading symbols from string/tst-strlcpy2...
(gdb) b strlcpy
Breakpoint 1 (strlcpy) pending.
(gdb) run --direct
Starting program: build-x86_64-linux/string/tst-strlcpy2 --direct

Breakpoint 1, __GI___strlcpy (dest=0x7ffff7fb7001 'Z' <repeats 200
times>...,
    src=0x7ffff7fbb001 " 7Ne|4Kby1H_v.E\\", size=16) at strlcpy.c:23
23 {

Generic version included in the following line.

IMPL (STRLCPY, 1)

$ string/tst-strlcpy2
                            strlcpy

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-27 19:10           ` Sunil Pandey
@ 2023-07-27 19:20             ` DJ Delorie
  2023-07-27 20:25               ` Sunil Pandey
  0 siblings, 1 reply; 12+ messages in thread
From: DJ Delorie @ 2023-07-27 19:20 UTC (permalink / raw)
  To: Sunil Pandey; +Cc: libc-alpha

Sunil Pandey <skpgkp2@gmail.com> writes:
> Actually, it's already testing the generic version of strlcpy
> implementation from the string/strlcpy.c file, so no need to implement
> another SIMPLE_* function.

While that is true at the moment, in the future someone will start
optimizing that function, add cpu-family-specific variants, etc.  Then,
when you're running on some core that chooses a vector-optimized
strlcpy, the simple function will be useful.

Note: I'm not saying "you must add this" as most of the other tests
don't either, and some test is better than no test.  I just don't want
you to make a decision based on me not explaining myself well enough ;-)

The easy path would have been to just add the IMPL line and have that
extra level of paranoia built-in from the start.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Add strlcpy/wcslcpy testcase
  2023-07-27 19:20             ` DJ Delorie
@ 2023-07-27 20:25               ` Sunil Pandey
  2023-07-27 23:20                 ` [PATCH v3] " Sunil K Pandey
  0 siblings, 1 reply; 12+ messages in thread
From: Sunil Pandey @ 2023-07-27 20:25 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

[-- Attachment #1: Type: text/plain, Size: 1664 bytes --]

On Thu, Jul 27, 2023 at 12:20 PM DJ Delorie <dj@redhat.com> wrote:

> Sunil Pandey <skpgkp2@gmail.com> writes:
> > Actually, it's already testing the generic version of strlcpy
> > implementation from the string/strlcpy.c file, so no need to implement
> > another SIMPLE_* function.
>
> While that is true at the moment, in the future someone will start
> optimizing that function, add cpu-family-specific variants, etc.  Then,
> when you're running on some core that chooses a vector-optimized
> strlcpy, the simple function will be useful.
>
> Note: I'm not saying "you must add this" as most of the other tests
> don't either, and some test is better than no test.  I just don't want
> you to make a decision based on me not explaining myself well enough ;-)
>
> The easy path would have been to just add the IMPL line and have that
> extra level of paranoia built-in from the start.
>
>
If someone has to optimize this function with the vector version, he has to
provide a fallback implementation to the generic scalar version. Generic
version shouldn't be duplicated and should map to (strlcpy)string/strlcpy.c
implementation. I have vector  implementation of this function that looks
like this.

$ string/tst-strlcpy2
   __strlcpy_evex __strlcpy_evex512 __strlcpy_avx2 __strlcpy_sse4_2
__strlcpy_generic

Where __strlcpy_generic is alias to strlcpy(implemented in
string/strlcpy.c).

Same is true for other flavors of strlcat/strlcpy/wcslcat and wcslcpy
functions.

I can certainly implement SIMPLE_* function but don't see any real use case.
In addition, it could be prone to error as you pointed out in my v1 patch.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v3] Add strlcpy/wcslcpy testcase
  2023-07-27 20:25               ` Sunil Pandey
@ 2023-07-27 23:20                 ` Sunil K Pandey
  2023-10-17 22:23                   ` DJ Delorie
  0 siblings, 1 reply; 12+ messages in thread
From: Sunil K Pandey @ 2023-07-27 23:20 UTC (permalink / raw)
  To: libc-alpha; +Cc: hjl.tools

This patch implements comprehensive tests for strlcpy/wcslcpy
functions.  Tests are mostly derived from strncpy test suites
and modified to incorporate strlcpy/wcslcpy specifications.

Changes from v1:

Rename test-strlcpy to tst-strlcpy2.
Rename test-wcslcpy to tst-wcslcpy2.
Remove unused SIMPLE_STRLCPY function.
Rearrange defines in alphabetical order.

Changes from v2:

Add back SIMPLE_STRLCPY function.
Rearrange defines.
---
 string/Makefile       |   1 +
 string/tst-strlcpy2.c | 322 ++++++++++++++++++++++++++++++++++++++++++
 wcsmbs/Makefile       |   1 +
 wcsmbs/tst-wcslcpy2.c |  20 +++
 4 files changed, 344 insertions(+)
 create mode 100644 string/tst-strlcpy2.c
 create mode 100644 wcsmbs/tst-wcslcpy2.c

diff --git a/string/Makefile b/string/Makefile
index 8cdfd5b000..cecaa796a8 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -199,6 +199,7 @@ tests := \
   tst-strfry \
   tst-strlcat \
   tst-strlcpy \
+  tst-strlcpy2 \
   tst-strlen \
   tst-strtok \
   tst-strtok_r \
diff --git a/string/tst-strlcpy2.c b/string/tst-strlcpy2.c
new file mode 100644
index 0000000000..fd6579f961
--- /dev/null
+++ b/string/tst-strlcpy2.c
@@ -0,0 +1,322 @@
+/* Test strlcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define TEST_MAIN
+#ifndef WIDE
+# define TEST_NAME "strlcpy"
+#else
+# define TEST_NAME "wcslcpy"
+#endif /* WIDE */
+#include "test-string.h"
+
+#ifdef WIDE
+# include <wchar.h>
+# define BIG_CHAR WCHAR_MAX
+# define CHAR wchar_t
+# define MEMCMP wmemcmp
+# define MEMSET wmemset
+# define SIMPLE_STRLCPY simple_wcslcpy
+# define SMALL_CHAR 1273
+# define STRLCPY wcslcpy
+# define STRLEN wcslen
+#else
+# define BIG_CHAR CHAR_MAX
+# define CHAR char
+# define MEMCMP memcmp
+# define MEMSET memset
+# define SIMPLE_STRLCPY simple_strlcpy
+# define SMALL_CHAR 127
+# define STRLCPY strlcpy
+# define STRLEN strlen
+#endif /* !WIDE */
+
+/* Naive implementation to verify results.  */
+size_t
+SIMPLE_STRLCPY (CHAR *dst, const CHAR *src, size_t n)
+{
+  size_t ret = STRLEN (src);
+
+  if (!n)
+    return ret;
+
+  while (--n)
+    if ((*dst++ = *src++) == '\0')
+      return ret;
+  *dst = '\0';
+  return ret;
+}
+
+IMPL (SIMPLE_STRLCPY, 0)
+IMPL (STRLCPY, 1)
+
+typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t);
+
+static void
+do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, size_t len, size_t n)
+{
+  if (CALL (impl, dst, src, n) != len)
+    {
+      error (0, 0, "Wrong result in function %s %zd %zd", impl->name,
+	     CALL (impl, dst, src, n), len);
+      ret = 1;
+      return;
+    }
+
+  if (n == 0)
+    return;
+
+  len = (len >= n ? n - 1 : len);
+  if (MEMCMP (dst, src, len) != 0)
+    {
+      error (0, 0, "Wrong result in function1 %s", impl->name);
+      ret = 1;
+      return;
+    }
+
+  if (dst [len] != '\0')
+    {
+      error (0, 0, "Wrong result in function2 %s", impl->name);
+      ret = 1;
+      return;
+    }
+}
+
+static void
+do_test (size_t align1, size_t align2, size_t len, size_t n, int max_char)
+{
+  size_t i;
+  CHAR *s1, *s2;
+
+  /* For wcslcpy: align1 and align2 here mean alignment not in bytes,
+     but in wchar_ts, in bytes it will equal to align * (sizeof (wchar_t)).  */
+  align1 &= 7;
+  if ((align1 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  align2 &= 7;
+  if ((align2 + len) * sizeof (CHAR) >= page_size)
+    return;
+
+  s1 = (CHAR *) (buf1) + align1;
+  s2 = (CHAR *) (buf2) + align2;
+
+  for (i = 0; i < len; ++i)
+    s1[i] = 32 + 23 * i % (max_char - 32);
+  s1[len] = 0;
+
+  FOR_EACH_IMPL (impl, 0)
+    do_one_test (impl, s2, s1, len, n);
+}
+
+static void
+do_page_tests (void)
+{
+  CHAR *s1, *s2;
+  const size_t maxoffset = 64;
+
+  /* Put s1 at the maxoffset from the edge of buf1's last page.  */
+  s1 = (CHAR *) buf1 + BUF1PAGES * page_size / sizeof(CHAR) - maxoffset;
+  /* s2 needs room to put a string with size of maxoffset + 1 at s2 +
+     (maxoffset - 1).  */
+  s2 = (CHAR *) buf2 + page_size / sizeof(CHAR) - maxoffset * 2;
+
+  MEMSET (s1, 'a', maxoffset - 1);
+  s1[maxoffset - 1] = '\0';
+
+  /* Both strings are bounded to a page with read/write access and the next
+     page is protected with PROT_NONE (meaning that any access outside of the
+     page regions will trigger an invalid memory access).
+
+     The loop copies the string s1 for all possible offsets up to maxoffset
+     for both inputs with a size larger than s1 (so memory access outside the
+     expected memory regions might trigger invalid access).  */
+
+  for (size_t off1 = 0; off1 < maxoffset; off1++)
+    {
+      for (size_t off2 = 0; off2 < maxoffset; off2++)
+	{
+	  FOR_EACH_IMPL (impl, 0)
+	    do_one_test (impl, s2 + off2, s1 + off1, maxoffset - off1 - 1,
+			 maxoffset + (maxoffset - off2));
+	}
+    }
+}
+
+static void
+do_random_tests (void)
+{
+  size_t i, j, n, align1, align2, len, size, mode;
+  CHAR *p1 = (CHAR *) (buf1 + page_size) - 1024;
+  CHAR *p2 = (CHAR *) (buf2 + page_size) - 1024;
+  size_t res;
+
+  for (n = 0; n < ITERATIONS; n++)
+    {
+      /* For wcslcpy: align1 and align2 here mean align not in bytes,
+	 but in wchar_ts, in bytes it will equal to align * (sizeof
+	 (wchar_t)).  */
+
+      mode = random ();
+      if (mode & 1)
+	{
+	  size = random () & 255;
+	  align1 = 512 - size - (random () & 15);
+	  if (mode & 2)
+	    align2 = align1 - (random () & 24);
+	  else
+	    align2 = align1 - (random () & 31);
+	  if (mode & 4)
+	    {
+	      j = align1;
+	      align1 = align2;
+	      align2 = j;
+	    }
+	  if (mode & 8)
+	    len = size - (random () & 31);
+	  else
+	    len = 512;
+	  if (len >= 512)
+	    len = random () & 511;
+	}
+      else
+	{
+	  align1 = random () & 31;
+	  if (mode & 2)
+	    align2 = random () & 31;
+	  else
+	    align2 = align1 + (random () & 24);
+	  len = random () & 511;
+	  j = align1;
+	  if (align2 > j)
+	    j = align2;
+	  if (mode & 4)
+	    {
+	      size = random () & 511;
+	      if (size + j > 512)
+		size = 512 - j - (random () & 31);
+	    }
+	  else
+	    size = 512 - j;
+	  if ((mode & 8) && len + j >= 512)
+	    len = 512 - j - (random () & 7);
+	}
+      j = len + align1;
+      for (i = 0; i < j; i++)
+	{
+	  p1[i] = random () & BIG_CHAR;
+	  if (i >= align1 && i < len + align1 && !p1[i])
+	    p1[i] = (random () & SMALL_CHAR) + 3;
+	}
+      p1[i] = 0;
+
+      FOR_EACH_IMPL (impl, 1)
+	{
+	  MEMSET (p2 - 64, '\1', 512 + 64);
+	  res = CALL (impl, (CHAR *) (p2 + align2),
+		      (CHAR *) (p1 + align1), size);
+	  if (res != len)
+	    {
+	      error (0, 0, "Iteration %zd - wrong result in function %s (%zd, %zd) %zd != %zd",
+		     n, impl->name, align1, align2, len, res);
+	      ret = 1;
+	    }
+	  for (j = 0; j < align2 + 64; ++j)
+	    {
+	      if (p2[j - 64] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage before, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = align2 + len + 1;
+	  if (size + align2 > j)
+	    j = size + align2;
+	  for (; j < 512; ++j)
+	    {
+	      if (p2[j] != '\1')
+		{
+		  error (0, 0, "Iteration %zd - garbage after, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	  j = len;
+	  /* Check for zero size.  */
+	  if (size)
+	    {
+	      if (size <= j)
+		j = size - 1;
+	      if (MEMCMP (p1 + align1, p2 + align2, j))
+		{
+		  error (0, 0, "Iteration %zd - different strings, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		}
+	      if (p2[align2 + j])
+		{
+		  error (0, 0, "Iteration %zd - garbage after size, %s (%zd, %zd, %zd)",
+			 n, impl->name, align1, align2, len);
+		  ret = 1;
+		  break;
+		}
+	    }
+	}
+    }
+}
+
+int
+test_main (void)
+{
+  size_t i;
+
+  test_init ();
+
+  printf ("%28s", "");
+  FOR_EACH_IMPL (impl, 0)
+    printf ("\t%s", impl->name);
+  putchar ('\n');
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (i, i, 16, 16, SMALL_CHAR);
+      do_test (i, i, 16, 16, BIG_CHAR);
+      do_test (i, 2 * i, 16, 16, SMALL_CHAR);
+      do_test (2 * i, i, 16, 16, BIG_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, SMALL_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 1 << i, 2 << i, BIG_CHAR);
+      do_test (2 * i, 8 - i, 2 << i, 1 << i, BIG_CHAR);
+    }
+
+  for (i = 1; i < 8; ++i)
+    {
+      do_test (0, 0, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (0, 0, 16 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 4 << i, 8 << i, SMALL_CHAR);
+      do_test (8 - i, 2 * i, 16 << i, 8 << i, SMALL_CHAR);
+    }
+
+  do_random_tests ();
+  do_page_tests ();
+  return ret;
+}
+
+#include <support/test-driver.c>
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index 431136b9c9..d7626b386d 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -180,6 +180,7 @@ tests := \
   tst-wcrtomb \
   tst-wcslcat \
   tst-wcslcpy \
+  tst-wcslcpy2 \
   tst-wcsnlen \
   tst-wcstod-nan-locale \
   tst-wcstod-nan-sign \
diff --git a/wcsmbs/tst-wcslcpy2.c b/wcsmbs/tst-wcslcpy2.c
new file mode 100644
index 0000000000..bec40ed135
--- /dev/null
+++ b/wcsmbs/tst-wcslcpy2.c
@@ -0,0 +1,20 @@
+/* Test wcslcpy functions.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define WIDE 1
+#include "../string/tst-strlcpy2.c"
-- 
2.41.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3] Add strlcpy/wcslcpy testcase
  2023-07-27 23:20                 ` [PATCH v3] " Sunil K Pandey
@ 2023-10-17 22:23                   ` DJ Delorie
  0 siblings, 0 replies; 12+ messages in thread
From: DJ Delorie @ 2023-10-17 22:23 UTC (permalink / raw)
  To: Sunil K Pandey; +Cc: libc-alpha, hjl.tools


Sunil K Pandey via Libc-alpha <libc-alpha@sourceware.org> writes:
> This patch implements comprehensive tests for strlcpy/wcslcpy
> functions.  Tests are mostly derived from strncpy test suites
> and modified to incorporate strlcpy/wcslcpy specifications.
>
> Changes from v1:
>
> Rename test-strlcpy to tst-strlcpy2.
> Rename test-wcslcpy to tst-wcslcpy2.
> Remove unused SIMPLE_STRLCPY function.
> Rearrange defines in alphabetical order.
>
> Changes from v2:
>
> Add back SIMPLE_STRLCPY function.
> Rearrange defines.

Sorry for the delay.

LGTM and committed.  Thanks!

Reviewed-by: DJ Delorie <dj@redhat.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-10-17 22:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-30 20:45 [PATCH] Add strlcpy/wcslcpy testcase Sunil K Pandey
2023-07-15  1:07 ` DJ Delorie
2023-07-17 18:50   ` Carlos O'Donell
2023-07-17 20:48     ` DJ Delorie
2023-07-27 17:51       ` [PATCH v2] " Sunil K Pandey
2023-07-27 17:55       ` [PATCH] " Sunil Pandey
2023-07-27 18:50         ` DJ Delorie
2023-07-27 19:10           ` Sunil Pandey
2023-07-27 19:20             ` DJ Delorie
2023-07-27 20:25               ` Sunil Pandey
2023-07-27 23:20                 ` [PATCH v3] " Sunil K Pandey
2023-10-17 22:23                   ` DJ Delorie

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).