public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] strlcpy and related functions
@ 2023-04-20 12:28 Florian Weimer
  2023-04-20 12:28 ` [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178] Florian Weimer
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Florian Weimer @ 2023-04-20 12:28 UTC (permalink / raw)
  To: libc-alpha

I split this repost somewhat differently, with the manual updates in a
separate commit, so that I can give proper attribution to Paul's work.

I went through the discussion again and wasn't sure if there was
anything regarding the actual implementation, beyond the fortify macro
usage (discussed with Siddhesh) and Paul's suggestion to open-code the
copying and counting (which I declined).

Thanks,
Florian

Florian Weimer (2):
  Implement strlcpy and strlcat [BZ #178]
  Add the wcslcpy, wcslcat functions

Paul Eggert (1):
  manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy

 NEWS                                          |  3 +
 debug/Makefile                                |  4 +
 debug/Versions                                |  6 ++
 debug/strlcat_chk.c                           | 31 ++++++
 debug/strlcpy_chk.c                           | 31 ++++++
 debug/tst-fortify.c                           | 48 ++++++++++
 debug/wcslcat_chk.c                           | 31 ++++++
 debug/wcslcpy_chk.c                           | 31 ++++++
 include/string.h                              |  4 +
 include/wchar.h                               |  5 +
 manual/maint.texi                             |  8 ++
 manual/string.texi                            | 96 ++++++++++++++++++-
 string/Makefile                               |  4 +
 string/Versions                               |  4 +
 string/bits/string_fortified.h                | 36 +++++++
 string/string.h                               | 13 +++
 string/strlcat.c                              | 59 ++++++++++++
 string/strlcpy.c                              | 46 +++++++++
 string/tst-strlcat.c                          | 84 ++++++++++++++++
 string/tst-strlcpy.c                          | 68 +++++++++++++
 sysdeps/mach/hurd/i386/libc.abilist           |  8 ++
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  8 ++
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |  8 ++
 sysdeps/unix/sysv/linux/arc/libc.abilist      |  8 ++
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  8 ++
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  8 ++
 sysdeps/unix/sysv/linux/csky/libc.abilist     |  8 ++
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |  8 ++
 sysdeps/unix/sysv/linux/i386/libc.abilist     |  8 ++
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |  8 ++
 .../sysv/linux/loongarch/lp64/libc.abilist    |  8 ++
 .../sysv/linux/m68k/coldfire/libc.abilist     |  8 ++
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  8 ++
 .../sysv/linux/microblaze/be/libc.abilist     |  8 ++
 .../sysv/linux/microblaze/le/libc.abilist     |  8 ++
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |  8 ++
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |  8 ++
 .../sysv/linux/mips/mips64/n32/libc.abilist   |  8 ++
 .../sysv/linux/mips/mips64/n64/libc.abilist   |  8 ++
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |  8 ++
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |  8 ++
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |  8 ++
 .../powerpc/powerpc32/nofpu/libc.abilist      |  8 ++
 .../linux/powerpc/powerpc64/be/libc.abilist   |  8 ++
 .../linux/powerpc/powerpc64/le/libc.abilist   |  8 ++
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |  8 ++
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |  8 ++
 .../unix/sysv/linux/s390/s390-32/libc.abilist |  8 ++
 .../unix/sysv/linux/s390/s390-64/libc.abilist |  8 ++
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  8 ++
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  8 ++
 .../sysv/linux/sparc/sparc32/libc.abilist     |  8 ++
 .../sysv/linux/sparc/sparc64/libc.abilist     |  8 ++
 .../unix/sysv/linux/x86_64/64/libc.abilist    |  8 ++
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |  8 ++
 wcsmbs/Makefile                               |  4 +
 wcsmbs/Versions                               |  2 +
 wcsmbs/bits/wchar2.h                          | 37 +++++++
 wcsmbs/tst-wcslcat.c                          | 93 ++++++++++++++++++
 wcsmbs/tst-wcslcpy.c                          | 78 +++++++++++++++
 wcsmbs/wchar.h                                | 13 +++
 wcsmbs/wcslcat.c                              | 60 ++++++++++++
 wcsmbs/wcslcpy.c                              | 46 +++++++++
 63 files changed, 1222 insertions(+), 3 deletions(-)
 create mode 100644 debug/strlcat_chk.c
 create mode 100644 debug/strlcpy_chk.c
 create mode 100644 debug/wcslcat_chk.c
 create mode 100644 debug/wcslcpy_chk.c
 create mode 100644 string/strlcat.c
 create mode 100644 string/strlcpy.c
 create mode 100644 string/tst-strlcat.c
 create mode 100644 string/tst-strlcpy.c
 create mode 100644 wcsmbs/tst-wcslcat.c
 create mode 100644 wcsmbs/tst-wcslcpy.c
 create mode 100644 wcsmbs/wcslcat.c
 create mode 100644 wcsmbs/wcslcpy.c


base-commit: 65cbd52174f5bc211dd655727c2239e25e55bfce
-- 
2.40.0


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

* [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178]
  2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
@ 2023-04-20 12:28 ` Florian Weimer
  2023-06-06  5:41   ` Siddhesh Poyarekar
  2023-04-20 12:28 ` [PATCH v2 2/3] Add the wcslcpy, wcslcat functions Florian Weimer
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2023-04-20 12:28 UTC (permalink / raw)
  To: libc-alpha

These functions are about to be added to POSIX, under Austin Group
issue 986.

The fortified strlcat implementation does not raise SIGABRT if the
destination buffer does not contain a null terminator, it just
inheritis the non-failing regular strlcat behavior.
---
 NEWS                                          |  3 +
 debug/Makefile                                |  2 +
 debug/Versions                                |  4 +
 debug/strlcat_chk.c                           | 31 +++++++
 debug/strlcpy_chk.c                           | 31 +++++++
 debug/tst-fortify.c                           | 31 +++++++
 include/string.h                              |  4 +
 string/Makefile                               |  4 +
 string/Versions                               |  4 +
 string/bits/string_fortified.h                | 36 ++++++++
 string/string.h                               | 13 +++
 string/strlcat.c                              | 59 +++++++++++++
 string/strlcpy.c                              | 46 ++++++++++
 string/tst-strlcat.c                          | 84 +++++++++++++++++++
 string/tst-strlcpy.c                          | 68 +++++++++++++++
 sysdeps/mach/hurd/i386/libc.abilist           |  4 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  4 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |  4 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |  4 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |  4 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |  4 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  4 +
 .../sysv/linux/microblaze/be/libc.abilist     |  4 +
 .../sysv/linux/microblaze/le/libc.abilist     |  4 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |  4 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |  4 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |  4 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |  4 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |  4 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |  4 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |  4 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |  4 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |  4 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |  4 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |  4 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |  4 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  4 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |  4 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |  4 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |  4 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |  4 +
 50 files changed, 560 insertions(+)
 create mode 100644 debug/strlcat_chk.c
 create mode 100644 debug/strlcpy_chk.c
 create mode 100644 string/strlcat.c
 create mode 100644 string/strlcpy.c
 create mode 100644 string/tst-strlcat.c
 create mode 100644 string/tst-strlcpy.c

diff --git a/NEWS b/NEWS
index 83d082afad..b21c4c10aa 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,9 @@ Major new features:
 
 * PRIb* and PRIB* macros from C2X have been added to <inttypes.h>.
 
+* The strlcpy and strlcat functions have been added.  They are derived
+  from OpenBSD, and are expected to be added to a future POSIX version.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * In the Linux kernel for the hppa/parisc architecture some of the
diff --git a/debug/Makefile b/debug/Makefile
index a8b4036cdc..f5f27f793c 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -83,6 +83,8 @@ routines = \
   stpncpy_chk \
   strcat_chk \
   strcpy_chk \
+  strlcat_chk \
+  strlcpy_chk \
   strncat_chk \
   strncpy_chk \
   swprintf_chk \
diff --git a/debug/Versions b/debug/Versions
index a6628db356..94dfa5f428 100644
--- a/debug/Versions
+++ b/debug/Versions
@@ -58,6 +58,10 @@ libc {
   GLIBC_2.25 {
     __explicit_bzero_chk;
   }
+  GLIBC_2.38 {
+    __strlcat_chk;
+    __strlcpy_chk;
+  }
   GLIBC_PRIVATE {
     __fortify_fail;
   }
diff --git a/debug/strlcat_chk.c b/debug/strlcat_chk.c
new file mode 100644
index 0000000000..888a62fad5
--- /dev/null
+++ b/debug/strlcat_chk.c
@@ -0,0 +1,31 @@
+/* Fortified version of strlcat.
+   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/>.  */
+
+#include <string.h>
+
+/* Check that the user-supplied size does not exceed the
+   compiler-determined size, and then forward to strlcat.  */
+size_t
+__strlcat_chk (char *__restrict s1, const char *__restrict s2,
+	       size_t n, size_t s1len)
+{
+  if (__glibc_unlikely (s1len < n))
+    __chk_fail ();
+
+  return __strlcat (s1, s2, n);
+}
diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
new file mode 100644
index 0000000000..768a3af686
--- /dev/null
+++ b/debug/strlcpy_chk.c
@@ -0,0 +1,31 @@
+/* Fortified version of strlcpy.
+   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/>.  */
+
+#include <string.h>
+
+/* Check that the user-supplied size does not exceed the
+   compiler-determined size, and then forward to strlcpy.  */
+size_t
+__strlcpy_chk (char *__restrict s1, const char *__restrict s2,
+	       size_t n, size_t s1len)
+{
+  if (__glibc_unlikely (s1len < n))
+    __chk_fail ();
+
+  return __strlcpy (s1, s2, n);
+}
diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
index 7850a4e558..f74a5e04dc 100644
--- a/debug/tst-fortify.c
+++ b/debug/tst-fortify.c
@@ -535,6 +535,20 @@ do_test (void)
   strncpy (a.buf1 + (O + 6), "X", l0 + 4);
   CHK_FAIL_END
 
+  CHK_FAIL_START
+  strlcpy (a.buf1 + (O + 6), "X", 4);
+  CHK_FAIL_END
+
+  CHK_FAIL_START
+  strlcpy (a.buf1 + (O + 6), "X", l0 + 4);
+  CHK_FAIL_END
+
+  {
+    char *volatile buf2 = buf;
+    if (strlcpy (buf2, "a", sizeof (buf) + 1) != 1)
+      FAIL ();
+  }
+
 # if !defined __cplusplus || defined __va_arg_pack
   CHK_FAIL_START
   sprintf (a.buf1 + (O + 7), "%d", num1);
@@ -558,6 +572,23 @@ do_test (void)
   CHK_FAIL_START
   strncat (a.buf1, "ZYXWV", l0 + 3);
   CHK_FAIL_END
+
+  memset (a.buf1, 0, sizeof (a.buf1));
+  CHK_FAIL_START
+  strlcat (a.buf1 + (O + 6), "X", 4);
+  CHK_FAIL_END
+
+  memset (a.buf1, 0, sizeof (a.buf1));
+  CHK_FAIL_START
+  strlcat (a.buf1 + (O + 6), "X", l0 + 4);
+  CHK_FAIL_END
+
+  {
+    buf[0] = '\0';
+    char *volatile buf2 = buf;
+    if (strlcat (buf2, "a", sizeof (buf) + 1) != 1)
+      FAIL ();
+  }
 #endif
 
 
diff --git a/include/string.h b/include/string.h
index 673cfd7272..0c78ad2539 100644
--- a/include/string.h
+++ b/include/string.h
@@ -88,6 +88,10 @@ libc_hidden_proto (__stpcpy)
 # define __stpcpy(dest, src) __builtin_stpcpy (dest, src)
 #endif
 libc_hidden_proto (__stpncpy)
+extern __typeof (strlcpy) __strlcpy;
+libc_hidden_proto (__strlcpy)
+extern __typeof (strlcat) __strlcat;
+libc_hidden_proto (__strlcat)
 libc_hidden_proto (__rawmemchr)
 libc_hidden_proto (__strcasecmp)
 libc_hidden_proto (__strcasecmp_l)
diff --git a/string/Makefile b/string/Makefile
index c84b49aaa5..c746ee1792 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -92,6 +92,8 @@ routines := \
   strerrorname_np \
   strfry \
   string-inlines \
+  strlcat \
+  strlcpy \
   strlen \
   strncase \
   strncase_l \
@@ -175,6 +177,8 @@ tests := \
   tst-inlcall \
   tst-memmove-overflow \
   tst-strfry \
+  tst-strlcat \
+  tst-strlcpy \
   tst-strlen \
   tst-strtok \
   tst-strtok_r \
diff --git a/string/Versions b/string/Versions
index 864c4cf7a4..c56e372a3c 100644
--- a/string/Versions
+++ b/string/Versions
@@ -92,4 +92,8 @@ libc {
   GLIBC_2.35 {
     __memcmpeq;
   }
+  GLIBC_2.38 {
+    strlcat;
+    strlcpy;
+  }
 }
diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
index 9900df6104..23ef064168 100644
--- a/string/bits/string_fortified.h
+++ b/string/bits/string_fortified.h
@@ -139,4 +139,40 @@ __NTH (strncat (char *__restrict __dest, const char *__restrict __src,
 				  __glibc_objsize (__dest));
 }
 
+#ifdef __USE_MISC
+extern size_t __strlcpy_chk (char *__dest, const char *__src, size_t __n,
+			     size_t __destlen) __THROW;
+extern size_t __REDIRECT_NTH (__strlcpy_alias,
+			      (char *__dest, const char *__src, size_t __n),
+			      strlcpy);
+
+__fortify_function size_t
+__NTH (strlcpy (char *__restrict __dest, const char *__restrict __src,
+		size_t __n))
+{
+  if (__glibc_objsize (__dest) != (size_t) -1
+      && (!__builtin_constant_p (__n > __glibc_objsize (__dest))
+	  || __n > __glibc_objsize (__dest)))
+    return __strlcpy_chk (__dest, __src, __n, __glibc_objsize (__dest));
+  return __strlcpy_alias (__dest, __src, __n);
+}
+
+extern size_t __strlcat_chk (char *__dest, const char *__src, size_t __n,
+			     size_t __destlen) __THROW;
+extern size_t __REDIRECT_NTH (__strlcat_alias,
+			      (char *__dest, const char *__src, size_t __n),
+			      strlcat);
+
+__fortify_function size_t
+__NTH (strlcat (char *__restrict __dest, const char *__restrict __src,
+		size_t __n))
+{
+  if (__glibc_objsize (__dest) != (size_t) -1
+      && (!__builtin_constant_p (__n > __glibc_objsize (__dest))
+	  || __n > __glibc_objsize (__dest)))
+    return __strlcat_chk (__dest, __src, __n, __glibc_objsize (__dest));
+  return __strlcat_alias (__dest, __src, __n);
+}
+#endif /* __USE_MISC */
+
 #endif /* bits/string_fortified.h */
diff --git a/string/string.h b/string/string.h
index 4927879ecf..c0773d11d8 100644
--- a/string/string.h
+++ b/string/string.h
@@ -501,6 +501,19 @@ extern char *stpncpy (char *__restrict __dest,
      __THROW __nonnull ((1, 2));
 #endif
 
+#ifdef __USE_MISC
+/* Copy at most N - 1 characters from SRC to DEST.  */
+extern size_t strlcpy (char *__restrict __dest,
+		       const char *__restrict __src, size_t __n)
+  __THROW __nonnull ((1, 2)) __attr_access ((__write_only__, 1, 3));
+
+/* Append SRC to DEST, possibly with truncation to keep the total size
+   below N.  */
+extern size_t strlcat (char *__restrict __dest,
+		       const char *__restrict __src, size_t __n)
+  __THROW __nonnull ((1, 2))  __attr_access ((__read_write__, 1, 3));
+#endif
+
 #ifdef	__USE_GNU
 /* Compare S1 and S2 as strings holding name & indices/version numbers.  */
 extern int strverscmp (const char *__s1, const char *__s2)
diff --git a/string/strlcat.c b/string/strlcat.c
new file mode 100644
index 0000000000..dce4c255d1
--- /dev/null
+++ b/string/strlcat.c
@@ -0,0 +1,59 @@
+/* Append a null-terminated string to another string, with length checking.
+   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/>.  */
+
+#include <stdint.h>
+#include <string.h>
+
+size_t
+__strlcat (char *__restrict dest, const char *__restrict src, size_t size)
+{
+  size_t src_length = strlen (src);
+
+  /* Our implementation strlcat supports dest == NULL if size == 0
+     (for consistency with snprintf and strlcpy), but strnlen does
+     not, so we have to cover this case explicitly.  */
+  if (size == 0)
+    return src_length;
+
+  size_t dest_length = __strnlen (dest, size);
+  if (dest_length != size)
+    {
+      /* Copy at most the remaining number of characters in the
+	 destination buffer.  Leave for the NUL terminator.  */
+      size_t to_copy = size - dest_length - 1;
+      /* But not more than what is available in the source string.  */
+      if (to_copy > src_length)
+	to_copy = src_length;
+
+      char *target = dest + dest_length;
+      memcpy (target, src, to_copy);
+      target[to_copy] = '\0';
+    }
+
+  /* If the sum wraps around, we have more than SIZE_MAX + 2 bytes in
+     the two input strings (including both null terminators).  If each
+     byte in the address space can be assigned a unique size_t value
+     (which the static_assert checks), then by the pigeonhole
+     principle, the two input strings must overlap, which is
+     undefined.  */
+  _Static_assert (sizeof (uintptr_t) == sizeof (size_t),
+		  "theoretical maximum object size covers address space");
+  return dest_length + src_length;
+}
+libc_hidden_def (__strlcat)
+weak_alias (__strlcat, strlcat)
diff --git a/string/strlcpy.c b/string/strlcpy.c
new file mode 100644
index 0000000000..7a0df3ebb6
--- /dev/null
+++ b/string/strlcpy.c
@@ -0,0 +1,46 @@
+/* Copy a null-terminated string to a fixed-size buffer, with length checking.
+   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/>.  */
+
+#include <string.h>
+
+size_t
+__strlcpy (char *__restrict dest, const char *__restrict src, size_t size)
+{
+  size_t src_length = strlen (src);
+
+  if (__glibc_unlikely (src_length >= size))
+    {
+      if (size > 0)
+	{
+	  /* Copy the leading portion of the string.  The last
+	     character is subsequently overwritten with the NUL
+	     terminator, but the destination size is usually a
+	     multiple of a small power of two, so writing it twice
+	     should be more efficient than copying an odd number of
+	     bytes.  */
+	  memcpy (dest, src, size);
+	  dest[size - 1] = '\0';
+	}
+    }
+  else
+    /* Copy the string and its terminating NUL character.  */
+    memcpy (dest, src, src_length + 1);
+  return src_length;
+}
+libc_hidden_def (__strlcpy)
+weak_alias (__strlcpy, strlcpy)
diff --git a/string/tst-strlcat.c b/string/tst-strlcat.c
new file mode 100644
index 0000000000..f8c716373e
--- /dev/null
+++ b/string/tst-strlcat.c
@@ -0,0 +1,84 @@
+/* Test the strlcat function.
+   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/>.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  struct {
+    char buf1[16];
+    char buf2[16];
+  } s;
+
+  /* Nothing is written to the destination if its size is 0.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcat (s.buf1, "", 0), 0);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+  TEST_COMPARE (strlcat (s.buf1, "Hello!", 0), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "He");
+  TEST_COMPARE (strlcat (s.buf1, "llo!", sizeof (s.buf1)), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "H");
+  TEST_COMPARE (strlcat (s.buf1, "ello, world!!!", sizeof (s.buf1)), 15);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* A source string one character longer than the destination buffer
+     is truncated by one character.  The total length is returned.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "Hello");
+  TEST_COMPARE (strlcat (s.buf1, ", world!!!!", sizeof (s.buf1)), 16);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* An even longer source string is truncated as well, and the total
+     length is returned.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "Hello,");
+  TEST_COMPARE (strlcat (s.buf1, " world!!!!!!!!", sizeof (s.buf1)), 20);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* A destination string which is not NUL-terminated does not result
+     in any changes to the buffer.  */
+  memset (&s, '@', sizeof (s));
+  memset (s.buf1, '$', sizeof (s.buf1));
+  TEST_COMPARE (strlcat (s.buf1, "", sizeof (s.buf1)), 16);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
+  TEST_COMPARE (strlcat (s.buf1, "Hello!", sizeof (s.buf1)), 22);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
+  TEST_COMPARE (strlcat (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)), 36);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/string/tst-strlcpy.c b/string/tst-strlcpy.c
new file mode 100644
index 0000000000..0063c43f5c
--- /dev/null
+++ b/string/tst-strlcpy.c
@@ -0,0 +1,68 @@
+/* Test the strlcpy function.
+   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/>.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  struct {
+    char buf1[16];
+    char buf2[16];
+  } s;
+
+  /* Nothing is written to the destination if its size is 0.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcpy (s.buf1, "Hello!", 0), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcpy (s.buf1, "Hello!", sizeof (s.buf1)), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!", sizeof (s.buf1)), 15);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* A source string one character longer than the destination buffer
+     is truncated by one character.  The untruncated source length is
+     returned.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!!", sizeof (s.buf1)), 16);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  /* An even longer source string is truncated as well, and the
+     original length is returned.  */
+  memset (&s, '@', sizeof (s));
+  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)), 20);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 6925222ff3..9b4c5f4719 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2326,6 +2326,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 0e2d9c3045..cf51b88932 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2665,3 +2665,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f1bec1978d..4b25f343b8 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2774,6 +2774,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index aa874b88d0..5a58cc0477 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2426,3 +2426,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index afbd57da6f..99ce948c5c 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -546,6 +546,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e7364cd3fe..c00bf72ebc 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -543,6 +543,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 913fa59215..71130f2c6b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2702,3 +2702,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 43af3a9811..5a651c03df 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2651,6 +2651,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index af72f8fab0..12b91ef632 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2835,6 +2835,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 48cbb0fa50..f223c5e08d 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2600,6 +2600,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index c15884bb0b..b91ed6e704 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2186,3 +2186,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 3738db81df..0d91d7f1ae 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -547,6 +547,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index ed13627752..e87b22747a 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2778,6 +2778,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 8357738621..f7623d6d72 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2751,3 +2751,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 58c5da583d..298aa99b42 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2748,3 +2748,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index d3741945cd..f83bdc50cd 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2743,6 +2743,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 5319fdc204..611ece2ac4 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2741,6 +2741,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 1743ea6eb9..0af286fda1 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2749,6 +2749,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 9b1f53c6ac..8285f2196e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2651,6 +2651,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index ae1c6ca1b5..c7144d7cd8 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2790,3 +2790,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index a7c572c947..bb43247795 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2172,3 +2172,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 074fa031a7..7cc5660830 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2817,6 +2817,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index dfcb4bd2d5..dd290af782 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2850,6 +2850,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 63bbccf3f9..f2b001402c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2571,6 +2571,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index ab85fd61ef..9cc431666e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2885,3 +2885,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index b716f5c763..b9b725f913 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2428,3 +2428,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 774e777b65..e0f4863856 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2628,3 +2628,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 8625135c48..8db68fcea7 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2815,6 +2815,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index d00c7eb262..ec9747b7ea 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2608,6 +2608,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index b63037241d..9576b818d8 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2658,6 +2658,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index d80055617d..b67b1b2bb5 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2655,6 +2655,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 5be55c11d2..b251fc9c69 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2810,6 +2810,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 475fdaae15..5ef9bbec34 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2623,6 +2623,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 6cfb928bc8..9ad800b62e 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2574,6 +2574,10 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index c735097172..6a3a66c5d4 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2680,3 +2680,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 __strlcat_chk F
+GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 strlcat F
+GLIBC_2.38 strlcpy F
-- 
2.40.0



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

* [PATCH v2 2/3] Add the wcslcpy, wcslcat functions
  2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
  2023-04-20 12:28 ` [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178] Florian Weimer
@ 2023-04-20 12:28 ` Florian Weimer
  2023-06-06  5:56   ` Siddhesh Poyarekar
  2023-04-20 12:28 ` [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy Florian Weimer
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2023-04-20 12:28 UTC (permalink / raw)
  To: libc-alpha

These functions are about to be added to POSIX, under Austin Group
issue 986.

The fortified versions use byte counters instead of character counts
for the compiler-generated size argument.  This is expected to result
in less generated code for dynamic object sizes because most allocation
functions use byte counts, not wide character counts.
---
 debug/Makefile                                |  2 +
 debug/Versions                                |  2 +
 debug/tst-fortify.c                           | 17 ++++
 debug/wcslcat_chk.c                           | 31 +++++++
 debug/wcslcpy_chk.c                           | 31 +++++++
 include/wchar.h                               |  5 +
 sysdeps/mach/hurd/i386/libc.abilist           |  4 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  4 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |  4 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |  4 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |  4 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |  4 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |  4 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  4 +
 .../sysv/linux/microblaze/be/libc.abilist     |  4 +
 .../sysv/linux/microblaze/le/libc.abilist     |  4 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |  4 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |  4 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |  4 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |  4 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |  4 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |  4 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |  4 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |  4 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |  4 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |  4 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |  4 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |  4 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |  4 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  4 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  4 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |  4 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |  4 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |  4 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |  4 +
 wcsmbs/Makefile                               |  4 +
 wcsmbs/Versions                               |  2 +
 wcsmbs/bits/wchar2.h                          | 37 ++++++++
 wcsmbs/tst-wcslcat.c                          | 93 +++++++++++++++++++
 wcsmbs/tst-wcslcpy.c                          | 78 ++++++++++++++++
 wcsmbs/wchar.h                                | 13 +++
 wcsmbs/wcslcat.c                              | 60 ++++++++++++
 wcsmbs/wcslcpy.c                              | 46 +++++++++
 49 files changed, 561 insertions(+)
 create mode 100644 debug/wcslcat_chk.c
 create mode 100644 debug/wcslcpy_chk.c
 create mode 100644 wcsmbs/tst-wcslcat.c
 create mode 100644 wcsmbs/tst-wcslcpy.c
 create mode 100644 wcsmbs/wcslcat.c
 create mode 100644 wcsmbs/wcslcpy.c

diff --git a/debug/Makefile b/debug/Makefile
index f5f27f793c..4f018abecf 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -104,6 +104,8 @@ routines = \
   wcrtomb_chk \
   wcscat_chk \
   wcscpy_chk \
+  wcslcat_chk \
+  wcslcpy_chk \
   wcsncat_chk \
   wcsncpy_chk \
   wcsnrtombs_chk \
diff --git a/debug/Versions b/debug/Versions
index 94dfa5f428..9cf2725992 100644
--- a/debug/Versions
+++ b/debug/Versions
@@ -61,6 +61,8 @@ libc {
   GLIBC_2.38 {
     __strlcat_chk;
     __strlcpy_chk;
+    __wcslcat_chk;
+    __wcslcpy_chk;
   }
   GLIBC_PRIVATE {
     __fortify_fail;
diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
index f74a5e04dc..9fe20627ee 100644
--- a/debug/tst-fortify.c
+++ b/debug/tst-fortify.c
@@ -782,6 +782,18 @@ do_test (void)
   wcsncpy (wbuf + 9, L"XABCDEFGH", 8);
   CHK_FAIL_END
 
+  CHK_FAIL_START
+  wcslcpy (wbuf + 7, L"X", 4);
+  CHK_FAIL_END
+
+  CHK_FAIL_START
+  wcslcpy (wbuf + 7, L"X", l0 + 4);
+  CHK_FAIL_END
+
+  CHK_FAIL_START
+  wcslcpy (wbuf + 9, L"XABCDEFGH", 8);
+  CHK_FAIL_END
+
   CHK_FAIL_START
   wcpncpy (wbuf + 9, L"XABCDEFGH", 8);
   CHK_FAIL_END
@@ -804,6 +816,11 @@ do_test (void)
   wcsncat (wbuf, L"ZYXWV", l0 + 3);
   CHK_FAIL_END
 
+  wmemcpy (wbuf, wstr1 + 4, 7);
+  CHK_FAIL_START
+  wcslcat (wbuf, L"ZYXWV", l0 + 11);
+  CHK_FAIL_END
+
   CHK_FAIL_START
   wmemcpy (wa.buf1 + 1, L"abcdefghij", 10);
   CHK_FAIL_END
diff --git a/debug/wcslcat_chk.c b/debug/wcslcat_chk.c
new file mode 100644
index 0000000000..5d63fba076
--- /dev/null
+++ b/debug/wcslcat_chk.c
@@ -0,0 +1,31 @@
+/* Fortified version of wcslcat.
+   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/>.  */
+
+#include <wchar.h>
+
+/* Check that the user-supplied size does not exceed the
+   compiler-determined size, and then forward to wcslcat.  */
+size_t
+__wcslcat_chk (wchar_t *__restrict s1, const wchar_t *__restrict s2,
+               size_t n, size_t s1len)
+{
+  if (__glibc_unlikely (s1len / sizeof (wchar_t) < n))
+    __chk_fail ();
+
+  return __wcslcat (s1, s2, n);
+}
diff --git a/debug/wcslcpy_chk.c b/debug/wcslcpy_chk.c
new file mode 100644
index 0000000000..ff7434b59a
--- /dev/null
+++ b/debug/wcslcpy_chk.c
@@ -0,0 +1,31 @@
+/* Fortified version of wcslcpy.
+   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/>.  */
+
+#include <wchar.h>
+
+/* Check that the user-supplied size does not exceed the
+   compiler-determined size, and then forward to wcslcpy.  */
+size_t
+__wcslcpy_chk (wchar_t *__restrict s1, const wchar_t *__restrict s2,
+	       size_t n, size_t s1len)
+{
+  if (__glibc_unlikely (s1len / sizeof (wchar_t) < n))
+    __chk_fail ();
+
+  return __wcslcpy (s1, s2, n);
+}
diff --git a/include/wchar.h b/include/wchar.h
index fafe7c8e9b..ff4ae52045 100644
--- a/include/wchar.h
+++ b/include/wchar.h
@@ -203,6 +203,8 @@ extern size_t __wcslen (const wchar_t *__s) __attribute_pure__;
 extern size_t __wcsnlen (const wchar_t *__s, size_t __maxlen)
      __attribute_pure__;
 extern wchar_t *__wcscat (wchar_t *dest, const wchar_t *src);
+extern __typeof (wcslcat) __wcslcat;
+libc_hidden_proto (__wcslcat)
 extern wint_t __btowc (int __c) attribute_hidden;
 extern int __mbsinit (const __mbstate_t *__ps);
 extern size_t __mbrtowc (wchar_t *__restrict __pwc,
@@ -237,8 +239,11 @@ extern wchar_t *__wcscpy (wchar_t *__restrict __dest,
 			  const wchar_t *__restrict __src)
 			  attribute_hidden __nonnull ((1, 2));
 libc_hidden_proto (__wcscpy)
+extern __typeof (wcslcpy) __wcslcpy;
+libc_hidden_proto (__wcslcpy)
 extern wchar_t *__wcsncpy (wchar_t *__restrict __dest,
 			   const wchar_t *__restrict __src, size_t __n);
+
 extern wchar_t *__wcpcpy (wchar_t *__dest, const wchar_t *__src);
 extern wchar_t *__wcpncpy (wchar_t *__dest, const wchar_t *__src,
 			   size_t __n);
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 9b4c5f4719..74a9f427b2 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2328,8 +2328,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index cf51b88932..c49363e70e 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2667,5 +2667,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 4b25f343b8..d6b1dcaae6 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2776,8 +2776,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 5a58cc0477..dfe0c3f7b6 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2428,5 +2428,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 99ce948c5c..6c75e5aa76 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -548,8 +548,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index c00bf72ebc..03d6f7ae2d 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -545,8 +545,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 71130f2c6b..d858c108c6 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2704,5 +2704,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 5a651c03df..82a14f8ace 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2653,8 +2653,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 12b91ef632..1950b15d5d 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2837,8 +2837,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index f223c5e08d..d0b9cb279b 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2602,8 +2602,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index b91ed6e704..e760a631dd 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2188,5 +2188,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 0d91d7f1ae..35785a3d5f 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -549,8 +549,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index e87b22747a..4ab2426e0a 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2780,8 +2780,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index f7623d6d72..38faa16232 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2753,5 +2753,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 298aa99b42..374d658988 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2750,5 +2750,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index f83bdc50cd..fcc5e88e91 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2745,8 +2745,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 611ece2ac4..01eb96cd93 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2743,8 +2743,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 0af286fda1..a2748b7b74 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2751,8 +2751,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 8285f2196e..0ae7ba499d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2653,8 +2653,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index c7144d7cd8..947495a0e2 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2792,5 +2792,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index bb43247795..115f1039e7 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2174,5 +2174,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 7cc5660830..19c4c325b0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2819,8 +2819,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index dd290af782..3e043c4044 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2852,8 +2852,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index f2b001402c..e4f3a766bb 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2573,8 +2573,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 9cc431666e..dafe1c4a59 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2887,5 +2887,9 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index b9b725f913..b9740a1afc 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2430,5 +2430,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index e0f4863856..e3b4656aa2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2630,5 +2630,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 8db68fcea7..84cb7a50ed 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2817,8 +2817,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index ec9747b7ea..33df3b1646 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2610,8 +2610,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 9576b818d8..94cbccd715 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2660,8 +2660,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index b67b1b2bb5..3bb316a787 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2657,8 +2657,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index b251fc9c69..6341b491b4 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2812,8 +2812,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 5ef9bbec34..8ed1ea2926 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2625,8 +2625,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 9ad800b62e..57cfcc2086 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2576,8 +2576,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 6a3a66c5d4..3f0a9f6d82 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2682,5 +2682,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
 GLIBC_2.38 __strlcat_chk F
 GLIBC_2.38 __strlcpy_chk F
+GLIBC_2.38 __wcslcat_chk F
+GLIBC_2.38 __wcslcpy_chk F
 GLIBC_2.38 strlcat F
 GLIBC_2.38 strlcpy F
+GLIBC_2.38 wcslcat F
+GLIBC_2.38 wcslcpy F
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
index 4aa43252d7..ac03437661 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -66,6 +66,8 @@ routines := \
   wcscpy \
   wcscspn \
   wcsdup \
+  wcslcat \
+  wcslcpy \
   wcslen \
   wcsmbsload \
   wcsncase \
@@ -146,6 +148,8 @@ tests := \
   tst-wchar-h \
   tst-wcpncpy \
   tst-wcrtomb \
+  tst-wcslcat \
+  tst-wcslcpy \
   tst-wcsnlen \
   tst-wcstod-nan-locale \
   tst-wcstod-nan-sign \
diff --git a/wcsmbs/Versions b/wcsmbs/Versions
index 2d9391348a..7bdfe43b4a 100644
--- a/wcsmbs/Versions
+++ b/wcsmbs/Versions
@@ -65,5 +65,7 @@ libc {
     __isoc23_vswscanf;
     __isoc23_vwscanf;
     __isoc23_wscanf;
+    wcslcat;
+    wcslcpy;
   }
 }
diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
index 8b41e6fbd6..02f44ab373 100644
--- a/wcsmbs/bits/wchar2.h
+++ b/wcsmbs/bits/wchar2.h
@@ -199,6 +199,43 @@ __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
   return __wcsncat_alias (__dest, __src, __n);
 }
 
+#ifdef __USE_MISC
+extern size_t __wcslcpy_chk (wchar_t *__dest, const wchar_t *__src, size_t __n,
+			     size_t __destlen) __THROW;
+extern size_t __REDIRECT_NTH (__wcslcpy_alias,
+			      (wchar_t *__dest, const wchar_t *__src,
+			       size_t __n), wcslcpy);
+
+__fortify_function size_t
+__NTH (wcslcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+		size_t __n))
+{
+  if (__glibc_objsize (__dest) != (size_t) -1
+      && (!__builtin_constant_p (__n
+				 > __glibc_objsize (__dest) / sizeof (wchar_t))
+	  || __n > __glibc_objsize (__dest) / sizeof (wchar_t)))
+    return __wcslcpy_chk (__dest, __src, __n, __glibc_objsize (__dest));
+  return __wcslcpy_alias (__dest, __src, __n);
+}
+
+extern size_t __wcslcat_chk (wchar_t *__dest, const wchar_t *__src, size_t __n,
+			     size_t __destlen) __THROW;
+extern size_t __REDIRECT_NTH (__wcslcat_alias,
+			      (wchar_t *__dest, const wchar_t *__src,
+			       size_t __n), wcslcat);
+
+__fortify_function size_t
+__NTH (wcslcat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+		size_t __n))
+{
+  if (__glibc_objsize (__dest) != (size_t) -1
+      && (!__builtin_constant_p (__n > __glibc_objsize (__dest)
+				 / sizeof (wchar_t))
+	  || __n > __glibc_objsize (__dest) / sizeof (wchar_t)))
+    return __wcslcat_chk (__dest, __src, __n, __glibc_objsize (__dest));
+  return __wcslcat_alias (__dest, __src, __n);
+}
+#endif /* __USE_MISC */
 
 
 extern int __REDIRECT_NTH_LDBL (__swprintf_alias,
diff --git a/wcsmbs/tst-wcslcat.c b/wcsmbs/tst-wcslcat.c
new file mode 100644
index 0000000000..63c3a164b5
--- /dev/null
+++ b/wcsmbs/tst-wcslcat.c
@@ -0,0 +1,93 @@
+/* Test the wcslcat function.
+   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/>.  */
+
+#include <array_length.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <wchar.h>
+
+static int
+do_test (void)
+{
+  struct {
+    wchar_t buf1[16];
+    wchar_t buf2[16];
+  } s;
+
+  /* Nothing is written to the destination if its size is 0.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcat (s.buf1, L"", 0), 0);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+  TEST_COMPARE (wcslcat (s.buf1, L"Hello!", 0), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  wcscpy (s.buf1, L"He");
+  TEST_COMPARE (wcslcat (s.buf1, L"llo!", array_length (s.buf1)), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  wcscpy (s.buf1, L"H");
+  TEST_COMPARE (wcslcat (s.buf1, L"ello, world!!!", array_length (s.buf1)),
+                15);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* A source string one character longer than the destination buffer
+     is truncated by one character.  The total length is returned.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  wcscpy (s.buf1, L"Hello");
+  TEST_COMPARE (wcslcat (s.buf1, L", world!!!!", array_length (s.buf1)), 16);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* An even longer source string is truncated as well, and the total
+     length is returned.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  wcscpy (s.buf1, L"Hello,");
+  TEST_COMPARE (wcslcat (s.buf1, L" world!!!!!!!!", array_length (s.buf1)),
+                20);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* A destination string which is not NUL-terminated does not result
+     in any changes to the buffer.  */
+  wmemset (s.buf1, '$', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcat (s.buf1, L"", array_length (s.buf1)), 16);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
+  TEST_COMPARE (wcslcat (s.buf1, L"Hello!", array_length (s.buf1)), 22);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
+  TEST_COMPARE (wcslcat (s.buf1, L"Hello, world!!!!!!!!",
+                         array_length (s.buf1)), 36);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/wcsmbs/tst-wcslcpy.c b/wcsmbs/tst-wcslcpy.c
new file mode 100644
index 0000000000..8eaffbf0c4
--- /dev/null
+++ b/wcsmbs/tst-wcslcpy.c
@@ -0,0 +1,78 @@
+/* Test the wcslcpy function.
+   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/>.  */
+
+#include <array_length.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <wchar.h>
+
+static int
+do_test (void)
+{
+  struct {
+    wchar_t buf1[16];
+    wchar_t buf2[16];
+  } s;
+
+  /* Nothing is written to the destination if its size is 0.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcpy (s.buf1, L"Hello!", 0), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcpy (s.buf1, L"Hello!", array_length (s.buf1)), 6);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+                     L"Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!", array_length (s.buf1)),
+                15);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* A source string one character longer than the destination buffer
+     is truncated by one character.  The untruncated source length is
+     returned.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!!", array_length (s.buf1)),
+                16);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  /* An even longer source string is truncated as well, and the
+     original length is returned.  */
+  wmemset (s.buf1, '@', array_length (s.buf1));
+  wmemset (s.buf2, '@', array_length (s.buf2));
+  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!!!!!!",
+                         array_length (s.buf1)), 20);
+  TEST_COMPARE_BLOB (&s, sizeof (s),
+                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
index acc2eb9ddf..6d15503830 100644
--- a/wcsmbs/wchar.h
+++ b/wcsmbs/wchar.h
@@ -93,6 +93,19 @@ extern wchar_t *wcsncpy (wchar_t *__restrict __dest,
 			 const wchar_t *__restrict __src, size_t __n)
      __THROW __nonnull ((1, 2));
 
+#ifdef __USE_MISC
+/* Copy at most N - 1 characters from SRC to DEST.  */
+extern size_t wcslcpy (wchar_t *__restrict __dest,
+		       const wchar_t *__restrict __src, size_t __n)
+  __THROW __nonnull ((1, 2)) __attr_access ((__write_only__, 1, 3));
+
+/* Append SRC to DEST, possibly with truncation to keep the total size
+   below N.  */
+extern size_t wcslcat (wchar_t *__restrict __dest,
+		       const wchar_t *__restrict __src, size_t __n)
+  __THROW __nonnull ((1, 2))  __attr_access ((__read_write__, 1, 3));
+#endif
+
 /* Append SRC onto DEST.  */
 extern wchar_t *wcscat (wchar_t *__restrict __dest,
 			const wchar_t *__restrict __src)
diff --git a/wcsmbs/wcslcat.c b/wcsmbs/wcslcat.c
new file mode 100644
index 0000000000..3bac6a2aa0
--- /dev/null
+++ b/wcsmbs/wcslcat.c
@@ -0,0 +1,60 @@
+/* Append a null-terminated wide string to another, with length checking.
+   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/>.  */
+
+#include <stdint.h>
+#include <wchar.h>
+
+size_t
+__wcslcat (wchar_t *__restrict dest, const wchar_t *__restrict src,
+           size_t size)
+{
+  size_t src_length = __wcslen (src);
+
+  /* Our implementation strlcat supports dest == NULL if size == 0
+     (for consistency with snprintf and strlcpy), but wcsnlen does
+     not, so we have to cover this case explicitly.  */
+  if (size == 0)
+    return src_length;
+
+  size_t dest_length = __wcsnlen (dest, size);
+  if (dest_length != size)
+    {
+      /* Copy at most the remaining number of characters in the
+	 destination buffer.  Leave for the null terminator.  */
+      size_t to_copy = size - dest_length - 1;
+      /* But not more than what is available in the source string.  */
+      if (to_copy > src_length)
+	to_copy = src_length;
+
+      wchar_t *target = dest + dest_length;
+      __wmemcpy (target, src, to_copy);
+      target[to_copy] = '\0';
+    }
+
+  /* If the sum wraps around, we have more than SIZE_MAX + 2 bytes in
+     the two input strings (including both null terminators).  If each
+     byte in the address space can be assigned a unique size_t value
+     (which the static_assert checks), then by the pigeonhole
+     principle, the two input strings must overlap, which is
+     undefined.  */
+  _Static_assert (sizeof (uintptr_t) == sizeof (size_t),
+		  "theoretical maximum object size covers address space");
+  return dest_length + src_length;
+}
+libc_hidden_def (__wcslcat)
+weak_alias (__wcslcat, wcslcat)
diff --git a/wcsmbs/wcslcpy.c b/wcsmbs/wcslcpy.c
new file mode 100644
index 0000000000..a1b1f1b43f
--- /dev/null
+++ b/wcsmbs/wcslcpy.c
@@ -0,0 +1,46 @@
+/* Copy a null-terminated wide string to a fixed-size buffer.
+   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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <wchar.h>
+
+size_t
+__wcslcpy (wchar_t *__restrict dest, const wchar_t *__restrict src, size_t size)
+{
+  size_t src_length = __wcslen (src);
+
+  if (__glibc_unlikely (src_length >= size))
+    {
+      if (size > 0)
+	{
+	  /* Copy the leading portion of the string.  The last
+	     character is subsequently overwritten with the null
+	     terminator, but the destination size is usually a
+	     multiple of a small power of two, so writing it twice
+	     should be more efficient than copying an odd number of
+	     character.  */
+	  __wmemcpy (dest, src, size);
+	  dest[size - 1] = '\0';
+	}
+    }
+  else
+    /* Copy the string and its terminating null character.  */
+    __wmemcpy (dest, src, src_length + 1);
+  return src_length;
+}
+libc_hidden_def (__wcslcpy)
+weak_alias (__wcslcpy, wcslcpy)
-- 
2.40.0



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

* [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy
  2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
  2023-04-20 12:28 ` [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178] Florian Weimer
  2023-04-20 12:28 ` [PATCH v2 2/3] Add the wcslcpy, wcslcat functions Florian Weimer
@ 2023-04-20 12:28 ` Florian Weimer
  2023-06-06  6:03   ` Siddhesh Poyarekar
  2023-04-20 16:19 ` [PATCH v2 0/3] strlcpy and related functions H.J. Lu
  2023-04-28 10:48 ` Florian Weimer
  4 siblings, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2023-04-20 12:28 UTC (permalink / raw)
  To: libc-alpha

From: Paul Eggert <eggert@cs.ucla.edu>

Co-authored-by: Florian Weimer <fweimer@redhat.com>
---
 manual/maint.texi  |  8 ++++
 manual/string.texi | 96 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 101 insertions(+), 3 deletions(-)

diff --git a/manual/maint.texi b/manual/maint.texi
index a8441e20b6..89da704f45 100644
--- a/manual/maint.texi
+++ b/manual/maint.texi
@@ -371,6 +371,10 @@ The following functions and macros are fortified in @theglibc{}:
 
 @item @code{strcpy}
 
+@item @code{strlcat}
+
+@item @code{strlcpy}
+
 @item @code{strncat}
 
 @item @code{strncpy}
@@ -411,6 +415,10 @@ The following functions and macros are fortified in @theglibc{}:
 
 @item @code{wcscpy}
 
+@item @code{wcslcat}
+
+@item @code{wcslcpy}
+
 @item @code{wcsncat}
 
 @item @code{wcsncpy}
diff --git a/manual/string.texi b/manual/string.texi
index ad57265274..4149d54ee7 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -726,8 +726,8 @@ This function has undefined results if the strings overlap.
 As noted below, this function has significant performance issues.
 @end deftypefun
 
-Programmers using the @code{strcat} or @code{wcscat} function (or the
-@code{strncat} or @code{wcsncat} functions defined in
+Programmers using the @code{strcat} or @code{wcscat} functions (or the
+@code{strlcat}, @code{strncat} and @code{wcsncat} functions defined in
 a later section, for that matter)
 can easily be recognized as lazy and reckless.  In almost all situations
 the lengths of the participating strings are known (it better should be
@@ -848,7 +848,8 @@ function.  The example would work for wide characters the same way.
 Whenever a programmer feels the need to use @code{strcat} she or he
 should think twice and look through the program to see whether the code cannot
 be rewritten to take advantage of already calculated results.
-The related functions @code{strncat} and @code{wcscat}
+The related functions @code{strlcat}, @code{strncat},
+@code{wcscat} and @code{wcsncat}
 are almost always unnecessary, too.
 Again: it is almost always unnecessary to use functions like @code{strcat}.
 
@@ -1076,6 +1077,95 @@ processing strings.  Also, this function has significant performance
 issues.  @xref{Concatenating Strings}.
 @end deftypefun
 
+@deftypefun size_t strlcpy (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
+@standards{BSD, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function copies the string @var{from} to the destination array
+@var{to}, limiting the result's size (including the null terminator)
+to @var{size}.  The caller should ensure that @var{size} includes room
+for the result's terminating null byte.
+
+If @var{size} is greater than the length of the string @var{from},
+this function copies the non-null bytes of the string
+@var{from} to the destination array @var{to},
+and terminates the copy with a null byte.  Like other
+string functions such as @code{strcpy}, but unlike @code{strncpy}, any
+remaining bytes in the destination array remain unchanged.
+
+If @var{size} is nonzero and less than or equal to the the length of the string
+@var{from}, this function copies only the first @samp{@var{size} - 1}
+bytes to the destination array @var{to}, and writes a terminating null
+byte to the last byte of the array.
+
+This function returns the length of the string @var{from}.  This means
+that truncation occurs if and only if the returned value is greater
+than or equal to @var{size}.
+
+The behavior is undefined if @var{to} or @var{from} is a null pointer,
+or if the destination array's size is less than @var{size}, or if the
+string @var{from} overlaps the first @var{size} bytes of the
+destination array.
+
+As noted below, this function is generally a poor choice for
+processing strings.  Also, this function has a performance issue,
+as its time cost is proportional to the length of @var{from}
+even when @var{size} is small.
+
+This function is derived from OpenBSD 2.4.
+@end deftypefun
+
+@deftypefun size_t wcslcpy (wchar_t *restrict @var{to}, const wchar_t *restrict @var{from}, size_t @var{size})
+@standards{BSD, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function is a variant of @code{strlcpy} for wide strings.
+The  @var{size} argument counts the length of the destination buffer in
+wide characters (and not bytes).
+
+This function is derived from BSD.
+@end deftypefun
+
+@deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
+@standards{BSD, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function appends the string @var{from} to the
+string @var{to}, limiting the result's total size (including the null
+terminator) to @var{size}.  The caller should ensure that @var{size}
+includes room for the result's terminating null byte.
+
+This function copies as much as possible of the string @var{from} into
+the array at @var{to} of @var{size} bytes, starting at the terminating
+null byte of the original string @var{to}.  In effect, this appends
+the string @var{from} to the string @var{to}.  Although the resulting
+string will contain a null terminator, it can be truncated (not all
+bytes in @var{from} may be copied).
+
+This function returns the sum of the original length of @var{to} and
+the length of @var{from}.  This means that truncation occurs if and
+only if the returned value is greater than or equal to @var{size}.
+
+The behavior is undefined if @var{to} or @var{from} is a null pointer,
+or if the destination array's size is less than @var{size}, or if the
+destination array does not contain a null byte in its first @var{size}
+bytes, or if the string @var{from} overlaps the first @var{size} bytes
+of the destination array.
+
+As noted below, this function is generally a poor choice for
+processing strings.  Also, this function has significant performance
+issues.  @xref{Concatenating Strings}.
+
+This function is derived from OpenBSD 2.4.
+@end deftypefun
+
+@deftypefun size_t wcslcat (wchar_t *restrict @var{to}, const wchar_t *restrict @var{from}, size_t @var{size})
+@standards{BSD, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+This function is a variant of @code{strlcat} for wide strings.
+The  @var{size} argument counts the length of the destination buffer in
+wide characters (and not bytes).
+
+This function is derived from BSD.
+@end deftypefun
+
 Because these functions can abruptly truncate strings or wide strings,
 they are generally poor choices for processing them.  When copying or
 concatening multibyte strings, they can truncate within a multibyte
-- 
2.40.0


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

* Re: [PATCH v2 0/3] strlcpy and related functions
  2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
                   ` (2 preceding siblings ...)
  2023-04-20 12:28 ` [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy Florian Weimer
@ 2023-04-20 16:19 ` H.J. Lu
  2023-04-21 17:46   ` Florian Weimer
  2023-04-28 10:48 ` Florian Weimer
  4 siblings, 1 reply; 14+ messages in thread
From: H.J. Lu @ 2023-04-20 16:19 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Thu, Apr 20, 2023 at 5:28 AM Florian Weimer via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> I split this repost somewhat differently, with the manual updates in a
> separate commit, so that I can give proper attribution to Paul's work.
>
> I went through the discussion again and wasn't sure if there was
> anything regarding the actual implementation, beyond the fortify macro
> usage (discussed with Siddhesh) and Paul's suggestion to open-code the
> copying and counting (which I declined).
>
> Thanks,
> Florian
>
> Florian Weimer (2):
>   Implement strlcpy and strlcat [BZ #178]
>   Add the wcslcpy, wcslcat functions
>
> Paul Eggert (1):
>   manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy
>
>  NEWS                                          |  3 +
>  debug/Makefile                                |  4 +
>  debug/Versions                                |  6 ++
>  debug/strlcat_chk.c                           | 31 ++++++
>  debug/strlcpy_chk.c                           | 31 ++++++
>  debug/tst-fortify.c                           | 48 ++++++++++
>  debug/wcslcat_chk.c                           | 31 ++++++
>  debug/wcslcpy_chk.c                           | 31 ++++++
>  include/string.h                              |  4 +
>  include/wchar.h                               |  5 +
>  manual/maint.texi                             |  8 ++
>  manual/string.texi                            | 96 ++++++++++++++++++-
>  string/Makefile                               |  4 +
>  string/Versions                               |  4 +
>  string/bits/string_fortified.h                | 36 +++++++
>  string/string.h                               | 13 +++
>  string/strlcat.c                              | 59 ++++++++++++
>  string/strlcpy.c                              | 46 +++++++++
>  string/tst-strlcat.c                          | 84 ++++++++++++++++
>  string/tst-strlcpy.c                          | 68 +++++++++++++
>  sysdeps/mach/hurd/i386/libc.abilist           |  8 ++
>  sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  8 ++
>  sysdeps/unix/sysv/linux/alpha/libc.abilist    |  8 ++
>  sysdeps/unix/sysv/linux/arc/libc.abilist      |  8 ++
>  sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  8 ++
>  sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  8 ++
>  sysdeps/unix/sysv/linux/csky/libc.abilist     |  8 ++
>  sysdeps/unix/sysv/linux/hppa/libc.abilist     |  8 ++
>  sysdeps/unix/sysv/linux/i386/libc.abilist     |  8 ++
>  sysdeps/unix/sysv/linux/ia64/libc.abilist     |  8 ++
>  .../sysv/linux/loongarch/lp64/libc.abilist    |  8 ++
>  .../sysv/linux/m68k/coldfire/libc.abilist     |  8 ++
>  .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  8 ++
>  .../sysv/linux/microblaze/be/libc.abilist     |  8 ++
>  .../sysv/linux/microblaze/le/libc.abilist     |  8 ++
>  .../sysv/linux/mips/mips32/fpu/libc.abilist   |  8 ++
>  .../sysv/linux/mips/mips32/nofpu/libc.abilist |  8 ++
>  .../sysv/linux/mips/mips64/n32/libc.abilist   |  8 ++
>  .../sysv/linux/mips/mips64/n64/libc.abilist   |  8 ++
>  sysdeps/unix/sysv/linux/nios2/libc.abilist    |  8 ++
>  sysdeps/unix/sysv/linux/or1k/libc.abilist     |  8 ++
>  .../linux/powerpc/powerpc32/fpu/libc.abilist  |  8 ++
>  .../powerpc/powerpc32/nofpu/libc.abilist      |  8 ++
>  .../linux/powerpc/powerpc64/be/libc.abilist   |  8 ++
>  .../linux/powerpc/powerpc64/le/libc.abilist   |  8 ++
>  .../unix/sysv/linux/riscv/rv32/libc.abilist   |  8 ++
>  .../unix/sysv/linux/riscv/rv64/libc.abilist   |  8 ++
>  .../unix/sysv/linux/s390/s390-32/libc.abilist |  8 ++
>  .../unix/sysv/linux/s390/s390-64/libc.abilist |  8 ++
>  sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  8 ++
>  sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  8 ++
>  .../sysv/linux/sparc/sparc32/libc.abilist     |  8 ++
>  .../sysv/linux/sparc/sparc64/libc.abilist     |  8 ++
>  .../unix/sysv/linux/x86_64/64/libc.abilist    |  8 ++
>  .../unix/sysv/linux/x86_64/x32/libc.abilist   |  8 ++
>  wcsmbs/Makefile                               |  4 +
>  wcsmbs/Versions                               |  2 +
>  wcsmbs/bits/wchar2.h                          | 37 +++++++
>  wcsmbs/tst-wcslcat.c                          | 93 ++++++++++++++++++
>  wcsmbs/tst-wcslcpy.c                          | 78 +++++++++++++++
>  wcsmbs/wchar.h                                | 13 +++
>  wcsmbs/wcslcat.c                              | 60 ++++++++++++
>  wcsmbs/wcslcpy.c                              | 46 +++++++++
>  63 files changed, 1222 insertions(+), 3 deletions(-)
>  create mode 100644 debug/strlcat_chk.c
>  create mode 100644 debug/strlcpy_chk.c
>  create mode 100644 debug/wcslcat_chk.c
>  create mode 100644 debug/wcslcpy_chk.c
>  create mode 100644 string/strlcat.c
>  create mode 100644 string/strlcpy.c
>  create mode 100644 string/tst-strlcat.c
>  create mode 100644 string/tst-strlcpy.c
>  create mode 100644 wcsmbs/tst-wcslcat.c
>  create mode 100644 wcsmbs/tst-wcslcpy.c
>  create mode 100644 wcsmbs/wcslcat.c
>  create mode 100644 wcsmbs/wcslcpy.c
>
>
> base-commit: 65cbd52174f5bc211dd655727c2239e25e55bfce
> --
> 2.40.0
>

Should we add benchtests for them?

-- 
H.J.

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

* Re: [PATCH v2 0/3] strlcpy and related functions
  2023-04-20 16:19 ` [PATCH v2 0/3] strlcpy and related functions H.J. Lu
@ 2023-04-21 17:46   ` Florian Weimer
  2023-04-22  0:25     ` Noah Goldstein
  0 siblings, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2023-04-21 17:46 UTC (permalink / raw)
  To: H.J. Lu; +Cc: libc-alpha

* H. J. Lu:

> Should we add benchtests for them?

Sure, but that's really not my area of expertise.

We could start with the strcpy tests if they exist, and pass down the
buffer sizes.  It would allow us to compare strcpy vs strlcpy, which
might be interesting in its own right.

I don't think it's necessary to benchmark the truncation case.

Thanks,
Florian


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

* Re: [PATCH v2 0/3] strlcpy and related functions
  2023-04-21 17:46   ` Florian Weimer
@ 2023-04-22  0:25     ` Noah Goldstein
  0 siblings, 0 replies; 14+ messages in thread
From: Noah Goldstein @ 2023-04-22  0:25 UTC (permalink / raw)
  To: Florian Weimer; +Cc: H.J. Lu, libc-alpha

On Fri, Apr 21, 2023 at 12:46 PM Florian Weimer via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> * H. J. Lu:
>
> > Should we add benchtests for them?
>
> Sure, but that's really not my area of expertise.
>
> We could start with the strcpy tests if they exist, and pass down the
> buffer sizes.  It would allow us to compare strcpy vs strlcpy, which
> might be interesting in its own right.
>
> I don't think it's necessary to benchmark the truncation case.
>

I posted a patch with benchmarks. Just reuses what we have for
strncpy/strncat. We can use that if you'd like.
> Thanks,
> Florian
>

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

* Re: [PATCH v2 0/3] strlcpy and related functions
  2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
                   ` (3 preceding siblings ...)
  2023-04-20 16:19 ` [PATCH v2 0/3] strlcpy and related functions H.J. Lu
@ 2023-04-28 10:48 ` Florian Weimer
  4 siblings, 0 replies; 14+ messages in thread
From: Florian Weimer @ 2023-04-28 10:48 UTC (permalink / raw)
  To: Florian Weimer via Libc-alpha

* Florian Weimer via Libc-alpha:

> I split this repost somewhat differently, with the manual updates in a
> separate commit, so that I can give proper attribution to Paul's work.
>
> I went through the discussion again and wasn't sure if there was
> anything regarding the actual implementation, beyond the fortify macro
> usage (discussed with Siddhesh) and Paul's suggestion to open-code the
> copying and counting (which I declined).
>
> Thanks,
> Florian
>
> Florian Weimer (2):
>   Implement strlcpy and strlcat [BZ #178]
>   Add the wcslcpy, wcslcat functions
>
> Paul Eggert (1):
>   manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy

Any further comments on this version?

Thanks,
Florian


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

* Re: [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178]
  2023-04-20 12:28 ` [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178] Florian Weimer
@ 2023-06-06  5:41   ` Siddhesh Poyarekar
  2023-06-14  9:04     ` Florian Weimer
  0 siblings, 1 reply; 14+ messages in thread
From: Siddhesh Poyarekar @ 2023-06-06  5:41 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 2023-04-20 08:28, Florian Weimer via Libc-alpha wrote:
> These functions are about to be added to POSIX, under Austin Group
> issue 986.
> 
> The fortified strlcat implementation does not raise SIGABRT if the
> destination buffer does not contain a null terminator, it just
> inheritis the non-failing regular strlcat behavior.

s/inheritis/inherits/ but you can edit it before you push, it doesn't 
need a respin.

Overall LGTM.  There is a minor question below, but it's not necessary 
to work on it in this patch, it could be a follow-up.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

> ---
>   NEWS                                          |  3 +
>   debug/Makefile                                |  2 +
>   debug/Versions                                |  4 +
>   debug/strlcat_chk.c                           | 31 +++++++
>   debug/strlcpy_chk.c                           | 31 +++++++
>   debug/tst-fortify.c                           | 31 +++++++
>   include/string.h                              |  4 +
>   string/Makefile                               |  4 +
>   string/Versions                               |  4 +
>   string/bits/string_fortified.h                | 36 ++++++++
>   string/string.h                               | 13 +++
>   string/strlcat.c                              | 59 +++++++++++++
>   string/strlcpy.c                              | 46 ++++++++++
>   string/tst-strlcat.c                          | 84 +++++++++++++++++++
>   string/tst-strlcpy.c                          | 68 +++++++++++++++
>   sysdeps/mach/hurd/i386/libc.abilist           |  4 +
>   sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  4 +
>   sysdeps/unix/sysv/linux/alpha/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/arc/libc.abilist      |  4 +
>   sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/csky/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/hppa/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/i386/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/ia64/libc.abilist     |  4 +
>   .../sysv/linux/loongarch/lp64/libc.abilist    |  4 +
>   .../sysv/linux/m68k/coldfire/libc.abilist     |  4 +
>   .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  4 +
>   .../sysv/linux/microblaze/be/libc.abilist     |  4 +
>   .../sysv/linux/microblaze/le/libc.abilist     |  4 +
>   .../sysv/linux/mips/mips32/fpu/libc.abilist   |  4 +
>   .../sysv/linux/mips/mips32/nofpu/libc.abilist |  4 +
>   .../sysv/linux/mips/mips64/n32/libc.abilist   |  4 +
>   .../sysv/linux/mips/mips64/n64/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/nios2/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/or1k/libc.abilist     |  4 +
>   .../linux/powerpc/powerpc32/fpu/libc.abilist  |  4 +
>   .../powerpc/powerpc32/nofpu/libc.abilist      |  4 +
>   .../linux/powerpc/powerpc64/be/libc.abilist   |  4 +
>   .../linux/powerpc/powerpc64/le/libc.abilist   |  4 +
>   .../unix/sysv/linux/riscv/rv32/libc.abilist   |  4 +
>   .../unix/sysv/linux/riscv/rv64/libc.abilist   |  4 +
>   .../unix/sysv/linux/s390/s390-32/libc.abilist |  4 +
>   .../unix/sysv/linux/s390/s390-64/libc.abilist |  4 +
>   sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  4 +
>   .../sysv/linux/sparc/sparc32/libc.abilist     |  4 +
>   .../sysv/linux/sparc/sparc64/libc.abilist     |  4 +
>   .../unix/sysv/linux/x86_64/64/libc.abilist    |  4 +
>   .../unix/sysv/linux/x86_64/x32/libc.abilist   |  4 +
>   50 files changed, 560 insertions(+)
>   create mode 100644 debug/strlcat_chk.c
>   create mode 100644 debug/strlcpy_chk.c
>   create mode 100644 string/strlcat.c
>   create mode 100644 string/strlcpy.c
>   create mode 100644 string/tst-strlcat.c
>   create mode 100644 string/tst-strlcpy.c
> 
> diff --git a/NEWS b/NEWS
> index 83d082afad..b21c4c10aa 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -21,6 +21,9 @@ Major new features:
>   
>   * PRIb* and PRIB* macros from C2X have been added to <inttypes.h>.
>   
> +* The strlcpy and strlcat functions have been added.  They are derived
> +  from OpenBSD, and are expected to be added to a future POSIX version.
> +

OK.

>   Deprecated and removed features, and other changes affecting compatibility:
>   
>   * In the Linux kernel for the hppa/parisc architecture some of the
> diff --git a/debug/Makefile b/debug/Makefile
> index a8b4036cdc..f5f27f793c 100644
> --- a/debug/Makefile
> +++ b/debug/Makefile
> @@ -83,6 +83,8 @@ routines = \
>     stpncpy_chk \
>     strcat_chk \
>     strcpy_chk \
> +  strlcat_chk \
> +  strlcpy_chk \
>     strncat_chk \
>     strncpy_chk \
>     swprintf_chk \
> diff --git a/debug/Versions b/debug/Versions
> index a6628db356..94dfa5f428 100644
> --- a/debug/Versions
> +++ b/debug/Versions
> @@ -58,6 +58,10 @@ libc {
>     GLIBC_2.25 {
>       __explicit_bzero_chk;
>     }
> +  GLIBC_2.38 {
> +    __strlcat_chk;
> +    __strlcpy_chk;
> +  }
>     GLIBC_PRIVATE {
>       __fortify_fail;
>     }

OK.

> diff --git a/debug/strlcat_chk.c b/debug/strlcat_chk.c
> new file mode 100644
> index 0000000000..888a62fad5
> --- /dev/null
> +++ b/debug/strlcat_chk.c
> @@ -0,0 +1,31 @@
> +/* Fortified version of strlcat.
> +   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/>.  */
> +
> +#include <string.h>
> +
> +/* Check that the user-supplied size does not exceed the
> +   compiler-determined size, and then forward to strlcat.  */
> +size_t
> +__strlcat_chk (char *__restrict s1, const char *__restrict s2,
> +	       size_t n, size_t s1len)
> +{
> +  if (__glibc_unlikely (s1len < n))
> +    __chk_fail ();
> +
> +  return __strlcat (s1, s2, n);
> +}

Hmm, I had not noticed the __restrict before, but since overlapping 
strings are undefined, this is OK.

> diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
> new file mode 100644
> index 0000000000..768a3af686
> --- /dev/null
> +++ b/debug/strlcpy_chk.c
> @@ -0,0 +1,31 @@
> +/* Fortified version of strlcpy.
> +   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/>.  */
> +
> +#include <string.h>
> +
> +/* Check that the user-supplied size does not exceed the
> +   compiler-determined size, and then forward to strlcpy.  */
> +size_t
> +__strlcpy_chk (char *__restrict s1, const char *__restrict s2,
> +	       size_t n, size_t s1len)
> +{
> +  if (__glibc_unlikely (s1len < n))
> +    __chk_fail ();
> +
> +  return __strlcpy (s1, s2, n);
> +}

OK.

> diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
> index 7850a4e558..f74a5e04dc 100644
> --- a/debug/tst-fortify.c
> +++ b/debug/tst-fortify.c
> @@ -535,6 +535,20 @@ do_test (void)
>     strncpy (a.buf1 + (O + 6), "X", l0 + 4);
>     CHK_FAIL_END
>   
> +  CHK_FAIL_START
> +  strlcpy (a.buf1 + (O + 6), "X", 4);
> +  CHK_FAIL_END
> +
> +  CHK_FAIL_START
> +  strlcpy (a.buf1 + (O + 6), "X", l0 + 4);
> +  CHK_FAIL_END
> +
> +  {
> +    char *volatile buf2 = buf;
> +    if (strlcpy (buf2, "a", sizeof (buf) + 1) != 1)
> +      FAIL ();
> +  }
> +
>   # if !defined __cplusplus || defined __va_arg_pack
>     CHK_FAIL_START
>     sprintf (a.buf1 + (O + 7), "%d", num1);
> @@ -558,6 +572,23 @@ do_test (void)
>     CHK_FAIL_START
>     strncat (a.buf1, "ZYXWV", l0 + 3);
>     CHK_FAIL_END
> +
> +  memset (a.buf1, 0, sizeof (a.buf1));
> +  CHK_FAIL_START
> +  strlcat (a.buf1 + (O + 6), "X", 4);
> +  CHK_FAIL_END
> +
> +  memset (a.buf1, 0, sizeof (a.buf1));
> +  CHK_FAIL_START
> +  strlcat (a.buf1 + (O + 6), "X", l0 + 4);
> +  CHK_FAIL_END
> +
> +  {
> +    buf[0] = '\0';
> +    char *volatile buf2 = buf;
> +    if (strlcat (buf2, "a", sizeof (buf) + 1) != 1)
> +      FAIL ();
> +  }
>   #endif
>   
>   

OK.

> diff --git a/include/string.h b/include/string.h
> index 673cfd7272..0c78ad2539 100644
> --- a/include/string.h
> +++ b/include/string.h
> @@ -88,6 +88,10 @@ libc_hidden_proto (__stpcpy)
>   # define __stpcpy(dest, src) __builtin_stpcpy (dest, src)
>   #endif
>   libc_hidden_proto (__stpncpy)
> +extern __typeof (strlcpy) __strlcpy;
> +libc_hidden_proto (__strlcpy)
> +extern __typeof (strlcat) __strlcat;
> +libc_hidden_proto (__strlcat)
>   libc_hidden_proto (__rawmemchr)
>   libc_hidden_proto (__strcasecmp)
>   libc_hidden_proto (__strcasecmp_l)
> diff --git a/string/Makefile b/string/Makefile
> index c84b49aaa5..c746ee1792 100644
> --- a/string/Makefile
> +++ b/string/Makefile
> @@ -92,6 +92,8 @@ routines := \
>     strerrorname_np \
>     strfry \
>     string-inlines \
> +  strlcat \
> +  strlcpy \
>     strlen \
>     strncase \
>     strncase_l \
> @@ -175,6 +177,8 @@ tests := \
>     tst-inlcall \
>     tst-memmove-overflow \
>     tst-strfry \
> +  tst-strlcat \
> +  tst-strlcpy \
>     tst-strlen \
>     tst-strtok \
>     tst-strtok_r \

OK.

> diff --git a/string/Versions b/string/Versions
> index 864c4cf7a4..c56e372a3c 100644
> --- a/string/Versions
> +++ b/string/Versions
> @@ -92,4 +92,8 @@ libc {
>     GLIBC_2.35 {
>       __memcmpeq;
>     }
> +  GLIBC_2.38 {
> +    strlcat;
> +    strlcpy;
> +  }
>   }

OK.

> diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
> index 9900df6104..23ef064168 100644
> --- a/string/bits/string_fortified.h
> +++ b/string/bits/string_fortified.h
> @@ -139,4 +139,40 @@ __NTH (strncat (char *__restrict __dest, const char *__restrict __src,
>   				  __glibc_objsize (__dest));
>   }
>   
> +#ifdef __USE_MISC
> +extern size_t __strlcpy_chk (char *__dest, const char *__src, size_t __n,
> +			     size_t __destlen) __THROW;
> +extern size_t __REDIRECT_NTH (__strlcpy_alias,
> +			      (char *__dest, const char *__src, size_t __n),
> +			      strlcpy);
> +
> +__fortify_function size_t
> +__NTH (strlcpy (char *__restrict __dest, const char *__restrict __src,
> +		size_t __n))
> +{
> +  if (__glibc_objsize (__dest) != (size_t) -1
> +      && (!__builtin_constant_p (__n > __glibc_objsize (__dest))
> +	  || __n > __glibc_objsize (__dest)))
> +    return __strlcpy_chk (__dest, __src, __n, __glibc_objsize (__dest));
> +  return __strlcpy_alias (__dest, __src, __n);
> +}
> +
> +extern size_t __strlcat_chk (char *__dest, const char *__src, size_t __n,
> +			     size_t __destlen) __THROW;
> +extern size_t __REDIRECT_NTH (__strlcat_alias,
> +			      (char *__dest, const char *__src, size_t __n),
> +			      strlcat);
> +
> +__fortify_function size_t
> +__NTH (strlcat (char *__restrict __dest, const char *__restrict __src,
> +		size_t __n))
> +{
> +  if (__glibc_objsize (__dest) != (size_t) -1
> +      && (!__builtin_constant_p (__n > __glibc_objsize (__dest))
> +	  || __n > __glibc_objsize (__dest)))
> +    return __strlcat_chk (__dest, __src, __n, __glibc_objsize (__dest));
> +  return __strlcat_alias (__dest, __src, __n);
> +}
> +#endif /* __USE_MISC */
> +
>   #endif /* bits/string_fortified.h */

OK.

> diff --git a/string/string.h b/string/string.h
> index 4927879ecf..c0773d11d8 100644
> --- a/string/string.h
> +++ b/string/string.h
> @@ -501,6 +501,19 @@ extern char *stpncpy (char *__restrict __dest,
>        __THROW __nonnull ((1, 2));
>   #endif
>   
> +#ifdef __USE_MISC
> +/* Copy at most N - 1 characters from SRC to DEST.  */
> +extern size_t strlcpy (char *__restrict __dest,
> +		       const char *__restrict __src, size_t __n)
> +  __THROW __nonnull ((1, 2)) __attr_access ((__write_only__, 1, 3));
> +
> +/* Append SRC to DEST, possibly with truncation to keep the total size
> +   below N.  */
> +extern size_t strlcat (char *__restrict __dest,
> +		       const char *__restrict __src, size_t __n)
> +  __THROW __nonnull ((1, 2))  __attr_access ((__read_write__, 1, 3));
> +#endif
> +
>   #ifdef	__USE_GNU
>   /* Compare S1 and S2 as strings holding name & indices/version numbers.  */
>   extern int strverscmp (const char *__s1, const char *__s2)

OK.

> diff --git a/string/strlcat.c b/string/strlcat.c
> new file mode 100644
> index 0000000000..dce4c255d1
> --- /dev/null
> +++ b/string/strlcat.c
> @@ -0,0 +1,59 @@
> +/* Append a null-terminated string to another string, with length checking.
> +   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/>.  */
> +
> +#include <stdint.h>
> +#include <string.h>
> +
> +size_t
> +__strlcat (char *__restrict dest, const char *__restrict src, size_t size)
> +{
> +  size_t src_length = strlen (src);
> +
> +  /* Our implementation strlcat supports dest == NULL if size == 0
> +     (for consistency with snprintf and strlcpy), but strnlen does
> +     not, so we have to cover this case explicitly.  */
> +  if (size == 0)
> +    return src_length;
> +
> +  size_t dest_length = __strnlen (dest, size);

Does __strnlen result in an ifunc call?  If not then we're probably 
missing out on some speedup.  Not a blocker though, something to think 
about as an add-on fix.

> +  if (dest_length != size)
> +    {
> +      /* Copy at most the remaining number of characters in the
> +	 destination buffer.  Leave for the NUL terminator.  */
> +      size_t to_copy = size - dest_length - 1;
> +      /* But not more than what is available in the source string.  */
> +      if (to_copy > src_length)
> +	to_copy = src_length;
> +
> +      char *target = dest + dest_length;
> +      memcpy (target, src, to_copy);
> +      target[to_copy] = '\0';
> +    }
> +
> +  /* If the sum wraps around, we have more than SIZE_MAX + 2 bytes in
> +     the two input strings (including both null terminators).  If each
> +     byte in the address space can be assigned a unique size_t value
> +     (which the static_assert checks), then by the pigeonhole
> +     principle, the two input strings must overlap, which is
> +     undefined.  */
> +  _Static_assert (sizeof (uintptr_t) == sizeof (size_t),
> +		  "theoretical maximum object size covers address space");
> +  return dest_length + src_length;
> +}
> +libc_hidden_def (__strlcat)
> +weak_alias (__strlcat, strlcat)
> diff --git a/string/strlcpy.c b/string/strlcpy.c
> new file mode 100644
> index 0000000000..7a0df3ebb6
> --- /dev/null
> +++ b/string/strlcpy.c
> @@ -0,0 +1,46 @@
> +/* Copy a null-terminated string to a fixed-size buffer, with length checking.
> +   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/>.  */
> +
> +#include <string.h>
> +
> +size_t
> +__strlcpy (char *__restrict dest, const char *__restrict src, size_t size)
> +{
> +  size_t src_length = strlen (src);
> +
> +  if (__glibc_unlikely (src_length >= size))
> +    {
> +      if (size > 0)
> +	{
> +	  /* Copy the leading portion of the string.  The last
> +	     character is subsequently overwritten with the NUL
> +	     terminator, but the destination size is usually a
> +	     multiple of a small power of two, so writing it twice
> +	     should be more efficient than copying an odd number of
> +	     bytes.  */
> +	  memcpy (dest, src, size);
> +	  dest[size - 1] = '\0';
> +	}
> +    }
> +  else
> +    /* Copy the string and its terminating NUL character.  */
> +    memcpy (dest, src, src_length + 1);
> +  return src_length;
> +}
> +libc_hidden_def (__strlcpy)
> +weak_alias (__strlcpy, strlcpy)

OK.

> diff --git a/string/tst-strlcat.c b/string/tst-strlcat.c
> new file mode 100644
> index 0000000000..f8c716373e
> --- /dev/null
> +++ b/string/tst-strlcat.c
> @@ -0,0 +1,84 @@
> +/* Test the strlcat function.
> +   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/>.  */
> +
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <support/check.h>
> +
> +static int
> +do_test (void)
> +{
> +  struct {
> +    char buf1[16];
> +    char buf2[16];
> +  } s;
> +
> +  /* Nothing is written to the destination if its size is 0.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcat (s.buf1, "", 0), 0);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +  TEST_COMPARE (strlcat (s.buf1, "Hello!", 0), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* No bytes are are modified in the target buffer if the source
> +     string is short enough.  */
> +  memset (&s, '@', sizeof (s));
> +  strcpy (s.buf1, "He");
> +  TEST_COMPARE (strlcat (s.buf1, "llo!", sizeof (s.buf1)), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* A source string which fits exactly into the destination buffer is
> +     not truncated.  */
> +  memset (&s, '@', sizeof (s));
> +  strcpy (s.buf1, "H");
> +  TEST_COMPARE (strlcat (s.buf1, "ello, world!!!", sizeof (s.buf1)), 15);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* A source string one character longer than the destination buffer
> +     is truncated by one character.  The total length is returned.  */
> +  memset (&s, '@', sizeof (s));
> +  strcpy (s.buf1, "Hello");
> +  TEST_COMPARE (strlcat (s.buf1, ", world!!!!", sizeof (s.buf1)), 16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* An even longer source string is truncated as well, and the total
> +     length is returned.  */
> +  memset (&s, '@', sizeof (s));
> +  strcpy (s.buf1, "Hello,");
> +  TEST_COMPARE (strlcat (s.buf1, " world!!!!!!!!", sizeof (s.buf1)), 20);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* A destination string which is not NUL-terminated does not result
> +     in any changes to the buffer.  */
> +  memset (&s, '@', sizeof (s));
> +  memset (s.buf1, '$', sizeof (s.buf1));
> +  TEST_COMPARE (strlcat (s.buf1, "", sizeof (s.buf1)), 16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
> +  TEST_COMPARE (strlcat (s.buf1, "Hello!", sizeof (s.buf1)), 22);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
> +  TEST_COMPARE (strlcat (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)), 36);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 32);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/string/tst-strlcpy.c b/string/tst-strlcpy.c
> new file mode 100644
> index 0000000000..0063c43f5c
> --- /dev/null
> +++ b/string/tst-strlcpy.c
> @@ -0,0 +1,68 @@
> +/* Test the strlcpy function.
> +   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/>.  */
> +
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <support/check.h>
> +
> +static int
> +do_test (void)
> +{
> +  struct {
> +    char buf1[16];
> +    char buf2[16];
> +  } s;
> +
> +  /* Nothing is written to the destination if its size is 0.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcpy (s.buf1, "Hello!", 0), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* No bytes are are modified in the target buffer if the source
> +     string is short enough.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcpy (s.buf1, "Hello!", sizeof (s.buf1)), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* A source string which fits exactly into the destination buffer is
> +     not truncated.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!", sizeof (s.buf1)), 15);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* A source string one character longer than the destination buffer
> +     is truncated by one character.  The untruncated source length is
> +     returned.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!!", sizeof (s.buf1)), 16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  /* An even longer source string is truncated as well, and the
> +     original length is returned.  */
> +  memset (&s, '@', sizeof (s));
> +  TEST_COMPARE (strlcpy (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)), 20);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 32);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index 6925222ff3..9b4c5f4719 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2326,6 +2326,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 0e2d9c3045..cf51b88932 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2665,3 +2665,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index f1bec1978d..4b25f343b8 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2774,6 +2774,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
> index aa874b88d0..5a58cc0477 100644
> --- a/sysdeps/unix/sysv/linux/arc/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
> @@ -2426,3 +2426,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> index afbd57da6f..99ce948c5c 100644
> --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> @@ -546,6 +546,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>   GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> index e7364cd3fe..c00bf72ebc 100644
> --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> @@ -543,6 +543,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>   GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
> index 913fa59215..71130f2c6b 100644
> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
> @@ -2702,3 +2702,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 43af3a9811..5a651c03df 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -2651,6 +2651,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index af72f8fab0..12b91ef632 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2835,6 +2835,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 48cbb0fa50..f223c5e08d 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -2600,6 +2600,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> index c15884bb0b..b91ed6e704 100644
> --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> @@ -2186,3 +2186,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 3738db81df..0d91d7f1ae 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -547,6 +547,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0x98
>   GLIBC_2.4 _IO_2_1_stdin_ D 0x98
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index ed13627752..e87b22747a 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -2778,6 +2778,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> index 8357738621..f7623d6d72 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> @@ -2751,3 +2751,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> index 58c5da583d..298aa99b42 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> @@ -2748,3 +2748,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index d3741945cd..f83bdc50cd 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -2743,6 +2743,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 5319fdc204..611ece2ac4 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -2741,6 +2741,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 1743ea6eb9..0af286fda1 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -2749,6 +2749,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 9b1f53c6ac..8285f2196e 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -2651,6 +2651,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index ae1c6ca1b5..c7144d7cd8 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2790,3 +2790,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> index a7c572c947..bb43247795 100644
> --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> @@ -2172,3 +2172,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 074fa031a7..7cc5660830 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -2817,6 +2817,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index dfcb4bd2d5..dd290af782 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2850,6 +2850,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> index 63bbccf3f9..f2b001402c 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -2571,6 +2571,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> index ab85fd61ef..9cc431666e 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2885,3 +2885,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> index b716f5c763..b9b725f913 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> @@ -2428,3 +2428,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 774e777b65..e0f4863856 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2628,3 +2628,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 8625135c48..8db68fcea7 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2815,6 +2815,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index d00c7eb262..ec9747b7ea 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -2608,6 +2608,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> index b63037241d..9576b818d8 100644
> --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> @@ -2658,6 +2658,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> index d80055617d..b67b1b2bb5 100644
> --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> @@ -2655,6 +2655,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 5be55c11d2..b251fc9c69 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -2810,6 +2810,10 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>   GLIBC_2.38 __nldbl___isoc23_vswscanf F
>   GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 475fdaae15..5ef9bbec34 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -2623,6 +2623,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 6cfb928bc8..9ad800b62e 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -2574,6 +2574,10 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index c735097172..6a3a66c5d4 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2680,3 +2680,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>   GLIBC_2.38 __isoc23_wcstoull_l F
>   GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 __strlcat_chk F
> +GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 strlcat F
> +GLIBC_2.38 strlcpy F

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

* Re: [PATCH v2 2/3] Add the wcslcpy, wcslcat functions
  2023-04-20 12:28 ` [PATCH v2 2/3] Add the wcslcpy, wcslcat functions Florian Weimer
@ 2023-06-06  5:56   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 14+ messages in thread
From: Siddhesh Poyarekar @ 2023-06-06  5:56 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 2023-04-20 08:28, Florian Weimer via Libc-alpha wrote:
> These functions are about to be added to POSIX, under Austin Group
> issue 986.
> 
> The fortified versions use byte counters instead of character counts
> for the compiler-generated size argument.  This is expected to result
> in less generated code for dynamic object sizes because most allocation
> functions use byte counts, not wide character counts.

Does it really matter?  You'd be scaling in the _chk function instead of 
outside, so there doesn't really seem anything worthwhile to gain.  Not 
to mention that it's a constant, power of 2 scaling, which any CPU worth 
its salt should just breeze through.

I feel like it'll become a wee bit harder to talk about these functions 
years later where we'd be like "the fortified variants of the wide char 
functions take in the size of the destination in number of wchars, 
except wcslcat and wcslcpy, which take in number of bytes", not to 
mention situations where we forget that wcslcat and wcslcpy are different.

The rest looks OK.

> ---
>   debug/Makefile                                |  2 +
>   debug/Versions                                |  2 +
>   debug/tst-fortify.c                           | 17 ++++
>   debug/wcslcat_chk.c                           | 31 +++++++
>   debug/wcslcpy_chk.c                           | 31 +++++++
>   include/wchar.h                               |  5 +
>   sysdeps/mach/hurd/i386/libc.abilist           |  4 +
>   sysdeps/unix/sysv/linux/aarch64/libc.abilist  |  4 +
>   sysdeps/unix/sysv/linux/alpha/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/arc/libc.abilist      |  4 +
>   sysdeps/unix/sysv/linux/arm/be/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/arm/le/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/csky/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/hppa/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/i386/libc.abilist     |  4 +
>   sysdeps/unix/sysv/linux/ia64/libc.abilist     |  4 +
>   .../sysv/linux/loongarch/lp64/libc.abilist    |  4 +
>   .../sysv/linux/m68k/coldfire/libc.abilist     |  4 +
>   .../unix/sysv/linux/m68k/m680x0/libc.abilist  |  4 +
>   .../sysv/linux/microblaze/be/libc.abilist     |  4 +
>   .../sysv/linux/microblaze/le/libc.abilist     |  4 +
>   .../sysv/linux/mips/mips32/fpu/libc.abilist   |  4 +
>   .../sysv/linux/mips/mips32/nofpu/libc.abilist |  4 +
>   .../sysv/linux/mips/mips64/n32/libc.abilist   |  4 +
>   .../sysv/linux/mips/mips64/n64/libc.abilist   |  4 +
>   sysdeps/unix/sysv/linux/nios2/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/or1k/libc.abilist     |  4 +
>   .../linux/powerpc/powerpc32/fpu/libc.abilist  |  4 +
>   .../powerpc/powerpc32/nofpu/libc.abilist      |  4 +
>   .../linux/powerpc/powerpc64/be/libc.abilist   |  4 +
>   .../linux/powerpc/powerpc64/le/libc.abilist   |  4 +
>   .../unix/sysv/linux/riscv/rv32/libc.abilist   |  4 +
>   .../unix/sysv/linux/riscv/rv64/libc.abilist   |  4 +
>   .../unix/sysv/linux/s390/s390-32/libc.abilist |  4 +
>   .../unix/sysv/linux/s390/s390-64/libc.abilist |  4 +
>   sysdeps/unix/sysv/linux/sh/be/libc.abilist    |  4 +
>   sysdeps/unix/sysv/linux/sh/le/libc.abilist    |  4 +
>   .../sysv/linux/sparc/sparc32/libc.abilist     |  4 +
>   .../sysv/linux/sparc/sparc64/libc.abilist     |  4 +
>   .../unix/sysv/linux/x86_64/64/libc.abilist    |  4 +
>   .../unix/sysv/linux/x86_64/x32/libc.abilist   |  4 +
>   wcsmbs/Makefile                               |  4 +
>   wcsmbs/Versions                               |  2 +
>   wcsmbs/bits/wchar2.h                          | 37 ++++++++
>   wcsmbs/tst-wcslcat.c                          | 93 +++++++++++++++++++
>   wcsmbs/tst-wcslcpy.c                          | 78 ++++++++++++++++
>   wcsmbs/wchar.h                                | 13 +++
>   wcsmbs/wcslcat.c                              | 60 ++++++++++++
>   wcsmbs/wcslcpy.c                              | 46 +++++++++
>   49 files changed, 561 insertions(+)
>   create mode 100644 debug/wcslcat_chk.c
>   create mode 100644 debug/wcslcpy_chk.c
>   create mode 100644 wcsmbs/tst-wcslcat.c
>   create mode 100644 wcsmbs/tst-wcslcpy.c
>   create mode 100644 wcsmbs/wcslcat.c
>   create mode 100644 wcsmbs/wcslcpy.c
> 
> diff --git a/debug/Makefile b/debug/Makefile
> index f5f27f793c..4f018abecf 100644
> --- a/debug/Makefile
> +++ b/debug/Makefile
> @@ -104,6 +104,8 @@ routines = \
>     wcrtomb_chk \
>     wcscat_chk \
>     wcscpy_chk \
> +  wcslcat_chk \
> +  wcslcpy_chk \
>     wcsncat_chk \
>     wcsncpy_chk \
>     wcsnrtombs_chk \
> diff --git a/debug/Versions b/debug/Versions
> index 94dfa5f428..9cf2725992 100644
> --- a/debug/Versions
> +++ b/debug/Versions
> @@ -61,6 +61,8 @@ libc {
>     GLIBC_2.38 {
>       __strlcat_chk;
>       __strlcpy_chk;
> +    __wcslcat_chk;
> +    __wcslcpy_chk;
>     }
>     GLIBC_PRIVATE {
>       __fortify_fail;

OK.

> diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
> index f74a5e04dc..9fe20627ee 100644
> --- a/debug/tst-fortify.c
> +++ b/debug/tst-fortify.c
> @@ -782,6 +782,18 @@ do_test (void)
>     wcsncpy (wbuf + 9, L"XABCDEFGH", 8);
>     CHK_FAIL_END
>   
> +  CHK_FAIL_START
> +  wcslcpy (wbuf + 7, L"X", 4);
> +  CHK_FAIL_END
> +
> +  CHK_FAIL_START
> +  wcslcpy (wbuf + 7, L"X", l0 + 4);
> +  CHK_FAIL_END
> +
> +  CHK_FAIL_START
> +  wcslcpy (wbuf + 9, L"XABCDEFGH", 8);
> +  CHK_FAIL_END
> +
>     CHK_FAIL_START
>     wcpncpy (wbuf + 9, L"XABCDEFGH", 8);
>     CHK_FAIL_END
> @@ -804,6 +816,11 @@ do_test (void)
>     wcsncat (wbuf, L"ZYXWV", l0 + 3);
>     CHK_FAIL_END
>   
> +  wmemcpy (wbuf, wstr1 + 4, 7);
> +  CHK_FAIL_START
> +  wcslcat (wbuf, L"ZYXWV", l0 + 11);
> +  CHK_FAIL_END
> +
>     CHK_FAIL_START
>     wmemcpy (wa.buf1 + 1, L"abcdefghij", 10);
>     CHK_FAIL_END

OK.

> diff --git a/debug/wcslcat_chk.c b/debug/wcslcat_chk.c
> new file mode 100644
> index 0000000000..5d63fba076
> --- /dev/null
> +++ b/debug/wcslcat_chk.c
> @@ -0,0 +1,31 @@
> +/* Fortified version of wcslcat.
> +   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/>.  */
> +
> +#include <wchar.h>
> +
> +/* Check that the user-supplied size does not exceed the
> +   compiler-determined size, and then forward to wcslcat.  */
> +size_t
> +__wcslcat_chk (wchar_t *__restrict s1, const wchar_t *__restrict s2,
> +               size_t n, size_t s1len)
> +{
> +  if (__glibc_unlikely (s1len / sizeof (wchar_t) < n))
> +    __chk_fail ();
> +
> +  return __wcslcat (s1, s2, n);
> +}
> diff --git a/debug/wcslcpy_chk.c b/debug/wcslcpy_chk.c
> new file mode 100644
> index 0000000000..ff7434b59a
> --- /dev/null
> +++ b/debug/wcslcpy_chk.c
> @@ -0,0 +1,31 @@
> +/* Fortified version of wcslcpy.
> +   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/>.  */
> +
> +#include <wchar.h>
> +
> +/* Check that the user-supplied size does not exceed the
> +   compiler-determined size, and then forward to wcslcpy.  */
> +size_t
> +__wcslcpy_chk (wchar_t *__restrict s1, const wchar_t *__restrict s2,
> +	       size_t n, size_t s1len)
> +{
> +  if (__glibc_unlikely (s1len / sizeof (wchar_t) < n))
> +    __chk_fail ();
> +
> +  return __wcslcpy (s1, s2, n);
> +}
> diff --git a/include/wchar.h b/include/wchar.h
> index fafe7c8e9b..ff4ae52045 100644
> --- a/include/wchar.h
> +++ b/include/wchar.h
> @@ -203,6 +203,8 @@ extern size_t __wcslen (const wchar_t *__s) __attribute_pure__;
>   extern size_t __wcsnlen (const wchar_t *__s, size_t __maxlen)
>        __attribute_pure__;
>   extern wchar_t *__wcscat (wchar_t *dest, const wchar_t *src);
> +extern __typeof (wcslcat) __wcslcat;
> +libc_hidden_proto (__wcslcat)
>   extern wint_t __btowc (int __c) attribute_hidden;
>   extern int __mbsinit (const __mbstate_t *__ps);
>   extern size_t __mbrtowc (wchar_t *__restrict __pwc,
> @@ -237,8 +239,11 @@ extern wchar_t *__wcscpy (wchar_t *__restrict __dest,
>   			  const wchar_t *__restrict __src)
>   			  attribute_hidden __nonnull ((1, 2));
>   libc_hidden_proto (__wcscpy)
> +extern __typeof (wcslcpy) __wcslcpy;
> +libc_hidden_proto (__wcslcpy)
>   extern wchar_t *__wcsncpy (wchar_t *__restrict __dest,
>   			   const wchar_t *__restrict __src, size_t __n);
> +
>   extern wchar_t *__wcpcpy (wchar_t *__dest, const wchar_t *__src);
>   extern wchar_t *__wcpncpy (wchar_t *__dest, const wchar_t *__src,
>   			   size_t __n);
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index 9b4c5f4719..74a9f427b2 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2328,8 +2328,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index cf51b88932..c49363e70e 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2667,5 +2667,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 4b25f343b8..d6b1dcaae6 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2776,8 +2776,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
> index 5a58cc0477..dfe0c3f7b6 100644
> --- a/sysdeps/unix/sysv/linux/arc/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
> @@ -2428,5 +2428,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> index 99ce948c5c..6c75e5aa76 100644
> --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> @@ -548,8 +548,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>   GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> index c00bf72ebc..03d6f7ae2d 100644
> --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> @@ -545,8 +545,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>   GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
> index 71130f2c6b..d858c108c6 100644
> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
> @@ -2704,5 +2704,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 5a651c03df..82a14f8ace 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -2653,8 +2653,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 12b91ef632..1950b15d5d 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2837,8 +2837,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index f223c5e08d..d0b9cb279b 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -2602,8 +2602,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> index b91ed6e704..e760a631dd 100644
> --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> @@ -2188,5 +2188,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 0d91d7f1ae..35785a3d5f 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -549,8 +549,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _Exit F
>   GLIBC_2.4 _IO_2_1_stderr_ D 0x98
>   GLIBC_2.4 _IO_2_1_stdin_ D 0x98
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index e87b22747a..4ab2426e0a 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -2780,8 +2780,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> index f7623d6d72..38faa16232 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> @@ -2753,5 +2753,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> index 298aa99b42..374d658988 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> @@ -2750,5 +2750,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index f83bdc50cd..fcc5e88e91 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -2745,8 +2745,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 611ece2ac4..01eb96cd93 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -2743,8 +2743,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 0af286fda1..a2748b7b74 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -2751,8 +2751,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 8285f2196e..0ae7ba499d 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -2653,8 +2653,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index c7144d7cd8..947495a0e2 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2792,5 +2792,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> index bb43247795..115f1039e7 100644
> --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> @@ -2174,5 +2174,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 7cc5660830..19c4c325b0 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -2819,8 +2819,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index dd290af782..3e043c4044 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2852,8 +2852,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> index f2b001402c..e4f3a766bb 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -2573,8 +2573,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> index 9cc431666e..dafe1c4a59 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2887,5 +2887,9 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> index b9b725f913..b9740a1afc 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> @@ -2430,5 +2430,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index e0f4863856..e3b4656aa2 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2630,5 +2630,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 8db68fcea7..84cb7a50ed 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2817,8 +2817,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index ec9747b7ea..33df3b1646 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -2610,8 +2610,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> index 9576b818d8..94cbccd715 100644
> --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> @@ -2660,8 +2660,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> index b67b1b2bb5..3bb316a787 100644
> --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> @@ -2657,8 +2657,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index b251fc9c69..6341b491b4 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -2812,8 +2812,12 @@ GLIBC_2.38 __nldbl___isoc23_vwscanf F
>   GLIBC_2.38 __nldbl___isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 _IO_fprintf F
>   GLIBC_2.4 _IO_printf F
>   GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 5ef9bbec34..8ed1ea2926 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -2625,8 +2625,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 9ad800b62e..57cfcc2086 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -2576,8 +2576,12 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
>   GLIBC_2.4 __confstr_chk F
>   GLIBC_2.4 __fgets_chk F
>   GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 6a3a66c5d4..3f0a9f6d82 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2682,5 +2682,9 @@ GLIBC_2.38 __isoc23_wcstoumax F
>   GLIBC_2.38 __isoc23_wscanf F
>   GLIBC_2.38 __strlcat_chk F
>   GLIBC_2.38 __strlcpy_chk F
> +GLIBC_2.38 __wcslcat_chk F
> +GLIBC_2.38 __wcslcpy_chk F
>   GLIBC_2.38 strlcat F
>   GLIBC_2.38 strlcpy F
> +GLIBC_2.38 wcslcat F
> +GLIBC_2.38 wcslcpy F
> diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
> index 4aa43252d7..ac03437661 100644
> --- a/wcsmbs/Makefile
> +++ b/wcsmbs/Makefile
> @@ -66,6 +66,8 @@ routines := \
>     wcscpy \
>     wcscspn \
>     wcsdup \
> +  wcslcat \
> +  wcslcpy \
>     wcslen \
>     wcsmbsload \
>     wcsncase \
> @@ -146,6 +148,8 @@ tests := \
>     tst-wchar-h \
>     tst-wcpncpy \
>     tst-wcrtomb \
> +  tst-wcslcat \
> +  tst-wcslcpy \
>     tst-wcsnlen \
>     tst-wcstod-nan-locale \
>     tst-wcstod-nan-sign \
> diff --git a/wcsmbs/Versions b/wcsmbs/Versions
> index 2d9391348a..7bdfe43b4a 100644
> --- a/wcsmbs/Versions
> +++ b/wcsmbs/Versions
> @@ -65,5 +65,7 @@ libc {
>       __isoc23_vswscanf;
>       __isoc23_vwscanf;
>       __isoc23_wscanf;
> +    wcslcat;
> +    wcslcpy;
>     }
>   }
> diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
> index 8b41e6fbd6..02f44ab373 100644
> --- a/wcsmbs/bits/wchar2.h
> +++ b/wcsmbs/bits/wchar2.h
> @@ -199,6 +199,43 @@ __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
>     return __wcsncat_alias (__dest, __src, __n);
>   }
>   
> +#ifdef __USE_MISC
> +extern size_t __wcslcpy_chk (wchar_t *__dest, const wchar_t *__src, size_t __n,
> +			     size_t __destlen) __THROW;
> +extern size_t __REDIRECT_NTH (__wcslcpy_alias,
> +			      (wchar_t *__dest, const wchar_t *__src,
> +			       size_t __n), wcslcpy);
> +
> +__fortify_function size_t
> +__NTH (wcslcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
> +		size_t __n))
> +{
> +  if (__glibc_objsize (__dest) != (size_t) -1
> +      && (!__builtin_constant_p (__n
> +				 > __glibc_objsize (__dest) / sizeof (wchar_t))
> +	  || __n > __glibc_objsize (__dest) / sizeof (wchar_t)))
> +    return __wcslcpy_chk (__dest, __src, __n, __glibc_objsize (__dest));
> +  return __wcslcpy_alias (__dest, __src, __n);
> +}
> +
> +extern size_t __wcslcat_chk (wchar_t *__dest, const wchar_t *__src, size_t __n,
> +			     size_t __destlen) __THROW;
> +extern size_t __REDIRECT_NTH (__wcslcat_alias,
> +			      (wchar_t *__dest, const wchar_t *__src,
> +			       size_t __n), wcslcat);
> +
> +__fortify_function size_t
> +__NTH (wcslcat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
> +		size_t __n))
> +{
> +  if (__glibc_objsize (__dest) != (size_t) -1
> +      && (!__builtin_constant_p (__n > __glibc_objsize (__dest)
> +				 / sizeof (wchar_t))
> +	  || __n > __glibc_objsize (__dest) / sizeof (wchar_t)))
> +    return __wcslcat_chk (__dest, __src, __n, __glibc_objsize (__dest));
> +  return __wcslcat_alias (__dest, __src, __n);
> +}
> +#endif /* __USE_MISC */
>   
>   
>   extern int __REDIRECT_NTH_LDBL (__swprintf_alias,
> diff --git a/wcsmbs/tst-wcslcat.c b/wcsmbs/tst-wcslcat.c
> new file mode 100644
> index 0000000000..63c3a164b5
> --- /dev/null
> +++ b/wcsmbs/tst-wcslcat.c
> @@ -0,0 +1,93 @@
> +/* Test the wcslcat function.
> +   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/>.  */
> +
> +#include <array_length.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <wchar.h>
> +
> +static int
> +do_test (void)
> +{
> +  struct {
> +    wchar_t buf1[16];
> +    wchar_t buf2[16];
> +  } s;
> +
> +  /* Nothing is written to the destination if its size is 0.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcat (s.buf1, L"", 0), 0);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +  TEST_COMPARE (wcslcat (s.buf1, L"Hello!", 0), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* No bytes are are modified in the target buffer if the source
> +     string is short enough.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  wcscpy (s.buf1, L"He");
> +  TEST_COMPARE (wcslcat (s.buf1, L"llo!", array_length (s.buf1)), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* A source string which fits exactly into the destination buffer is
> +     not truncated.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  wcscpy (s.buf1, L"H");
> +  TEST_COMPARE (wcslcat (s.buf1, L"ello, world!!!", array_length (s.buf1)),
> +                15);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* A source string one character longer than the destination buffer
> +     is truncated by one character.  The total length is returned.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  wcscpy (s.buf1, L"Hello");
> +  TEST_COMPARE (wcslcat (s.buf1, L", world!!!!", array_length (s.buf1)), 16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* An even longer source string is truncated as well, and the total
> +     length is returned.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  wcscpy (s.buf1, L"Hello,");
> +  TEST_COMPARE (wcslcat (s.buf1, L" world!!!!!!!!", array_length (s.buf1)),
> +                20);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +		     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* A destination string which is not NUL-terminated does not result
> +     in any changes to the buffer.  */
> +  wmemset (s.buf1, '$', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcat (s.buf1, L"", array_length (s.buf1)), 16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
> +  TEST_COMPARE (wcslcat (s.buf1, L"Hello!", array_length (s.buf1)), 22);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
> +  TEST_COMPARE (wcslcat (s.buf1, L"Hello, world!!!!!!!!",
> +                         array_length (s.buf1)), 36);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", 128);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/wcsmbs/tst-wcslcpy.c b/wcsmbs/tst-wcslcpy.c
> new file mode 100644
> index 0000000000..8eaffbf0c4
> --- /dev/null
> +++ b/wcsmbs/tst-wcslcpy.c
> @@ -0,0 +1,78 @@
> +/* Test the wcslcpy function.
> +   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/>.  */
> +
> +#include <array_length.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <wchar.h>
> +
> +static int
> +do_test (void)
> +{
> +  struct {
> +    wchar_t buf1[16];
> +    wchar_t buf2[16];
> +  } s;
> +
> +  /* Nothing is written to the destination if its size is 0.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcpy (s.buf1, L"Hello!", 0), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s), L"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* No bytes are are modified in the target buffer if the source
> +     string is short enough.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcpy (s.buf1, L"Hello!", array_length (s.buf1)), 6);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +                     L"Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* A source string which fits exactly into the destination buffer is
> +     not truncated.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!", array_length (s.buf1)),
> +                15);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* A source string one character longer than the destination buffer
> +     is truncated by one character.  The untruncated source length is
> +     returned.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!!", array_length (s.buf1)),
> +                16);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  /* An even longer source string is truncated as well, and the
> +     original length is returned.  */
> +  wmemset (s.buf1, '@', array_length (s.buf1));
> +  wmemset (s.buf2, '@', array_length (s.buf2));
> +  TEST_COMPARE (wcslcpy (s.buf1, L"Hello, world!!!!!!!!",
> +                         array_length (s.buf1)), 20);
> +  TEST_COMPARE_BLOB (&s, sizeof (s),
> +                     L"Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@", 128);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
> index acc2eb9ddf..6d15503830 100644
> --- a/wcsmbs/wchar.h
> +++ b/wcsmbs/wchar.h
> @@ -93,6 +93,19 @@ extern wchar_t *wcsncpy (wchar_t *__restrict __dest,
>   			 const wchar_t *__restrict __src, size_t __n)
>        __THROW __nonnull ((1, 2));
>   
> +#ifdef __USE_MISC
> +/* Copy at most N - 1 characters from SRC to DEST.  */
> +extern size_t wcslcpy (wchar_t *__restrict __dest,
> +		       const wchar_t *__restrict __src, size_t __n)
> +  __THROW __nonnull ((1, 2)) __attr_access ((__write_only__, 1, 3));
> +
> +/* Append SRC to DEST, possibly with truncation to keep the total size
> +   below N.  */
> +extern size_t wcslcat (wchar_t *__restrict __dest,
> +		       const wchar_t *__restrict __src, size_t __n)
> +  __THROW __nonnull ((1, 2))  __attr_access ((__read_write__, 1, 3));
> +#endif
> +
>   /* Append SRC onto DEST.  */
>   extern wchar_t *wcscat (wchar_t *__restrict __dest,
>   			const wchar_t *__restrict __src)
> diff --git a/wcsmbs/wcslcat.c b/wcsmbs/wcslcat.c
> new file mode 100644
> index 0000000000..3bac6a2aa0
> --- /dev/null
> +++ b/wcsmbs/wcslcat.c
> @@ -0,0 +1,60 @@
> +/* Append a null-terminated wide string to another, with length checking.
> +   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/>.  */
> +
> +#include <stdint.h>
> +#include <wchar.h>
> +
> +size_t
> +__wcslcat (wchar_t *__restrict dest, const wchar_t *__restrict src,
> +           size_t size)
> +{
> +  size_t src_length = __wcslen (src);
> +
> +  /* Our implementation strlcat supports dest == NULL if size == 0
> +     (for consistency with snprintf and strlcpy), but wcsnlen does
> +     not, so we have to cover this case explicitly.  */
> +  if (size == 0)
> +    return src_length;
> +
> +  size_t dest_length = __wcsnlen (dest, size);
> +  if (dest_length != size)
> +    {
> +      /* Copy at most the remaining number of characters in the
> +	 destination buffer.  Leave for the null terminator.  */
> +      size_t to_copy = size - dest_length - 1;
> +      /* But not more than what is available in the source string.  */
> +      if (to_copy > src_length)
> +	to_copy = src_length;
> +
> +      wchar_t *target = dest + dest_length;
> +      __wmemcpy (target, src, to_copy);
> +      target[to_copy] = '\0';
> +    }
> +
> +  /* If the sum wraps around, we have more than SIZE_MAX + 2 bytes in
> +     the two input strings (including both null terminators).  If each
> +     byte in the address space can be assigned a unique size_t value
> +     (which the static_assert checks), then by the pigeonhole
> +     principle, the two input strings must overlap, which is
> +     undefined.  */
> +  _Static_assert (sizeof (uintptr_t) == sizeof (size_t),
> +		  "theoretical maximum object size covers address space");
> +  return dest_length + src_length;
> +}
> +libc_hidden_def (__wcslcat)
> +weak_alias (__wcslcat, wcslcat)
> diff --git a/wcsmbs/wcslcpy.c b/wcsmbs/wcslcpy.c
> new file mode 100644
> index 0000000000..a1b1f1b43f
> --- /dev/null
> +++ b/wcsmbs/wcslcpy.c
> @@ -0,0 +1,46 @@
> +/* Copy a null-terminated wide string to a fixed-size buffer.
> +   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
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <wchar.h>
> +
> +size_t
> +__wcslcpy (wchar_t *__restrict dest, const wchar_t *__restrict src, size_t size)
> +{
> +  size_t src_length = __wcslen (src);
> +
> +  if (__glibc_unlikely (src_length >= size))
> +    {
> +      if (size > 0)
> +	{
> +	  /* Copy the leading portion of the string.  The last
> +	     character is subsequently overwritten with the null
> +	     terminator, but the destination size is usually a
> +	     multiple of a small power of two, so writing it twice
> +	     should be more efficient than copying an odd number of
> +	     character.  */
> +	  __wmemcpy (dest, src, size);
> +	  dest[size - 1] = '\0';
> +	}
> +    }
> +  else
> +    /* Copy the string and its terminating null character.  */
> +    __wmemcpy (dest, src, src_length + 1);
> +  return src_length;
> +}
> +libc_hidden_def (__wcslcpy)
> +weak_alias (__wcslcpy, wcslcpy)

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

* Re: [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy
  2023-04-20 12:28 ` [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy Florian Weimer
@ 2023-06-06  6:03   ` Siddhesh Poyarekar
  2023-06-06 12:27     ` Florian Weimer
  0 siblings, 1 reply; 14+ messages in thread
From: Siddhesh Poyarekar @ 2023-06-06  6:03 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 2023-04-20 08:28, Florian Weimer via Libc-alpha wrote:
> From: Paul Eggert <eggert@cs.ucla.edu>
> 
> Co-authored-by: Florian Weimer <fweimer@redhat.com>
> ---
>   manual/maint.texi  |  8 ++++
>   manual/string.texi | 96 ++++++++++++++++++++++++++++++++++++++++++++--
>   2 files changed, 101 insertions(+), 3 deletions(-)
> 
> diff --git a/manual/maint.texi b/manual/maint.texi
> index a8441e20b6..89da704f45 100644
> --- a/manual/maint.texi
> +++ b/manual/maint.texi
> @@ -371,6 +371,10 @@ The following functions and macros are fortified in @theglibc{}:
>   
>   @item @code{strcpy}
>   
> +@item @code{strlcat}
> +
> +@item @code{strlcpy}
> +
>   @item @code{strncat}
>   
>   @item @code{strncpy}
> @@ -411,6 +415,10 @@ The following functions and macros are fortified in @theglibc{}:
>   
>   @item @code{wcscpy}
>   
> +@item @code{wcslcat}
> +
> +@item @code{wcslcpy}
> +
>   @item @code{wcsncat}
>   
>   @item @code{wcsncpy}
> diff --git a/manual/string.texi b/manual/string.texi
> index ad57265274..4149d54ee7 100644
> --- a/manual/string.texi
> +++ b/manual/string.texi
> @@ -726,8 +726,8 @@ This function has undefined results if the strings overlap.
>   As noted below, this function has significant performance issues.
>   @end deftypefun
>   
> -Programmers using the @code{strcat} or @code{wcscat} function (or the
> -@code{strncat} or @code{wcsncat} functions defined in
> +Programmers using the @code{strcat} or @code{wcscat} functions (or the
> +@code{strlcat}, @code{strncat} and @code{wcsncat} functions defined in
>   a later section, for that matter)
>   can easily be recognized as lazy and reckless.  In almost all situations
>   the lengths of the participating strings are known (it better should be
> @@ -848,7 +848,8 @@ function.  The example would work for wide characters the same way.
>   Whenever a programmer feels the need to use @code{strcat} she or he
>   should think twice and look through the program to see whether the code cannot
>   be rewritten to take advantage of already calculated results.
> -The related functions @code{strncat} and @code{wcscat}
> +The related functions @code{strlcat}, @code{strncat},
> +@code{wcscat} and @code{wcsncat}
>   are almost always unnecessary, too.
>   Again: it is almost always unnecessary to use functions like @code{strcat}.
>   
> @@ -1076,6 +1077,95 @@ processing strings.  Also, this function has significant performance
>   issues.  @xref{Concatenating Strings}.
>   @end deftypefun
>   
> +@deftypefun size_t strlcpy (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
> +@standards{BSD, string.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function copies the string @var{from} to the destination array
> +@var{to}, limiting the result's size (including the null terminator)
> +to @var{size}.  The caller should ensure that @var{size} includes room
> +for the result's terminating null byte.
> +
> +If @var{size} is greater than the length of the string @var{from},
> +this function copies the non-null bytes of the string
> +@var{from} to the destination array @var{to},
> +and terminates the copy with a null byte.  Like other
> +string functions such as @code{strcpy}, but unlike @code{strncpy}, any
> +remaining bytes in the destination array remain unchanged.
> +
> +If @var{size} is nonzero and less than or equal to the the length of the string
> +@var{from}, this function copies only the first @samp{@var{size} - 1}
> +bytes to the destination array @var{to}, and writes a terminating null
> +byte to the last byte of the array.
> +
> +This function returns the length of the string @var{from}.  This means
> +that truncation occurs if and only if the returned value is greater
> +than or equal to @var{size}.
> +
> +The behavior is undefined if @var{to} or @var{from} is a null pointer,
> +or if the destination array's size is less than @var{size}, or if the
> +string @var{from} overlaps the first @var{size} bytes of the
> +destination array.

Shouldn't this be undefined for all kinds of overlaps between @var{to} 
and @var{from} and not just when the @{from} overlaps with the first 
@var{size} bytes of @var{to}?  Also, perhaps s/destination 
array/@var{to}/ to make it clearer.

> +
> +As noted below, this function is generally a poor choice for
> +processing strings.  Also, this function has a performance issue,
> +as its time cost is proportional to the length of @var{from}
> +even when @var{size} is small.
> +
> +This function is derived from OpenBSD 2.4.
> +@end deftypefun
> +
> +@deftypefun size_t wcslcpy (wchar_t *restrict @var{to}, const wchar_t *restrict @var{from}, size_t @var{size})
> +@standards{BSD, string.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function is a variant of @code{strlcpy} for wide strings.
> +The  @var{size} argument counts the length of the destination buffer in
> +wide characters (and not bytes).
> +
> +This function is derived from BSD.
> +@end deftypefun
> +
> +@deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
> +@standards{BSD, string.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function appends the string @var{from} to the
> +string @var{to}, limiting the result's total size (including the null
> +terminator) to @var{size}.  The caller should ensure that @var{size}
> +includes room for the result's terminating null byte.
> +
> +This function copies as much as possible of the string @var{from} into
> +the array at @var{to} of @var{size} bytes, starting at the terminating
> +null byte of the original string @var{to}.  In effect, this appends
> +the string @var{from} to the string @var{to}.  Although the resulting
> +string will contain a null terminator, it can be truncated (not all
> +bytes in @var{from} may be copied).
> +
> +This function returns the sum of the original length of @var{to} and
> +the length of @var{from}.  This means that truncation occurs if and
> +only if the returned value is greater than or equal to @var{size}.
> +
> +The behavior is undefined if @var{to} or @var{from} is a null pointer,
> +or if the destination array's size is less than @var{size}, or if the
> +destination array does not contain a null byte in its first @var{size}
> +bytes, or if the string @var{from} overlaps the first @var{size} bytes
> +of the destination array.

Same question about overlaps, shouldn't we specify all overlaps as 
undefined?

> +
> +As noted below, this function is generally a poor choice for
> +processing strings.  Also, this function has significant performance
> +issues.  @xref{Concatenating Strings}.
> +
> +This function is derived from OpenBSD 2.4.
> +@end deftypefun
> +
> +@deftypefun size_t wcslcat (wchar_t *restrict @var{to}, const wchar_t *restrict @var{from}, size_t @var{size})
> +@standards{BSD, string.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function is a variant of @code{strlcat} for wide strings.
> +The  @var{size} argument counts the length of the destination buffer in
> +wide characters (and not bytes).
> +
> +This function is derived from BSD.
> +@end deftypefun
> +
>   Because these functions can abruptly truncate strings or wide strings,
>   they are generally poor choices for processing them.  When copying or
>   concatening multibyte strings, they can truncate within a multibyte

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

* Re: [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy
  2023-06-06  6:03   ` Siddhesh Poyarekar
@ 2023-06-06 12:27     ` Florian Weimer
  2023-06-06 12:42       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2023-06-06 12:27 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

* Siddhesh Poyarekar:

>> +The behavior is undefined if @var{to} or @var{from} is a null pointer,
>> +or if the destination array's size is less than @var{size}, or if the
>> +string @var{from} overlaps the first @var{size} bytes of the
>> +destination array.
>
> Shouldn't this be undefined for all kinds of overlaps between @var{to}
> and @var{from} and not just when the @{from} overlaps with the first
> @var{size} bytes of @var{to}?

I don't think so.  There is no reason why the data couldn't be copied
within the same array.  This can plausibly happen if a custom memory
allocator is used, for example.

> Also, perhaps s/destination array/@var{to}/ to make it clearer.

I don't think so because it's confusing whether the size refers to TO
itself or (conceptually) to *TO.

Thanks,
Florian


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

* Re: [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy
  2023-06-06 12:27     ` Florian Weimer
@ 2023-06-06 12:42       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 14+ messages in thread
From: Siddhesh Poyarekar @ 2023-06-06 12:42 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On 2023-06-06 08:27, Florian Weimer wrote:
> * Siddhesh Poyarekar:
> 
>>> +The behavior is undefined if @var{to} or @var{from} is a null pointer,
>>> +or if the destination array's size is less than @var{size}, or if the
>>> +string @var{from} overlaps the first @var{size} bytes of the
>>> +destination array.
>>
>> Shouldn't this be undefined for all kinds of overlaps between @var{to}
>> and @var{from} and not just when the @{from} overlaps with the first
>> @var{size} bytes of @var{to}?
> 
> I don't think so.  There is no reason why the data couldn't be copied
> within the same array.  This can plausibly happen if a custom memory
> allocator is used, for example.

Uhmm, I haven't heard of the customer allocator argument being used in 
this context but I suppose it makes sense.  I'm probably just getting 
jitters about specifying behaviour to that much detail when a simple 
"behavior is undefined if FROM and TO overlap" would suffice.  I mean 
this argument could be made for memcpy or any function that assumes 
non-aliased inputs.

>> Also, perhaps s/destination array/@var{to}/ to make it clearer.
> 
> I don't think so because it's confusing whether the size refers to TO
> itself or (conceptually) to *TO.

Fair enough.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>

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

* Re: [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178]
  2023-06-06  5:41   ` Siddhesh Poyarekar
@ 2023-06-14  9:04     ` Florian Weimer
  0 siblings, 0 replies; 14+ messages in thread
From: Florian Weimer @ 2023-06-14  9:04 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

* Siddhesh Poyarekar:

>> +size_t
>> +__strlcat (char *__restrict dest, const char *__restrict src, size_t size)
>> +{
>> +  size_t src_length = strlen (src);
>> +
>> +  /* Our implementation strlcat supports dest == NULL if size == 0
>> +     (for consistency with snprintf and strlcpy), but strnlen does
>> +     not, so we have to cover this case explicitly.  */
>> +  if (size == 0)
>> +    return src_length;
>> +
>> +  size_t dest_length = __strnlen (dest, size);
>
> Does __strnlen result in an ifunc call?  If not then we're probably
> missing out on some speedup.  Not a blocker though, something to think
> about as an add-on fix.

This is architecture-dependent, I think.  Some architectures prefer
direct calls over IFUNC dispatch.

Targets with IFUNCs will likely get IFUNCs for strlcat and strlcpy as
well.

Thanks,
Florian


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

end of thread, other threads:[~2023-06-14  9:04 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-20 12:28 [PATCH v2 0/3] strlcpy and related functions Florian Weimer
2023-04-20 12:28 ` [PATCH v2 1/3] Implement strlcpy and strlcat [BZ #178] Florian Weimer
2023-06-06  5:41   ` Siddhesh Poyarekar
2023-06-14  9:04     ` Florian Weimer
2023-04-20 12:28 ` [PATCH v2 2/3] Add the wcslcpy, wcslcat functions Florian Weimer
2023-06-06  5:56   ` Siddhesh Poyarekar
2023-04-20 12:28 ` [PATCH v2 3/3] manual: Manual update for strlcat, strlcpy, wcslcat, wclscpy Florian Weimer
2023-06-06  6:03   ` Siddhesh Poyarekar
2023-06-06 12:27     ` Florian Weimer
2023-06-06 12:42       ` Siddhesh Poyarekar
2023-04-20 16:19 ` [PATCH v2 0/3] strlcpy and related functions H.J. Lu
2023-04-21 17:46   ` Florian Weimer
2023-04-22  0:25     ` Noah Goldstein
2023-04-28 10:48 ` Florian Weimer

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).