diff mbox series

[2/2] Add the wcslcpy, wcslcat functions

Message ID 6c94849aad1952b50504894b35fa462d445c3a4f.1680693362.git.fweimer@redhat.com
State New
Headers show
Series strlcpy/strlcat/wcslcpy/wcscat implementation | expand

Commit Message

Florian Weimer April 5, 2023, 11:20 a.m. UTC
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.
---
 NEWS                                          |  1 +
 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 +
 manual/string.texi                            | 20 ++++
 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                               |  8 +-
 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 +++++++++
 51 files changed, 583 insertions(+), 3 deletions(-)
 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

Comments

Paul Eggert April 8, 2023, 10:09 p.m. UTC | #1
Although I didn't look at the wcslcpy/wcslcat code carefully, it clearly 
has problems similar to those of the strlcpy/strlcat code and should be 
fixed the same way. Nobody uses these functions, so their performance 
does not matter. Certainly we should not try to optimize them.

The manual could use a bit of fixup; please see attached.
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 60b40fabcf..24b26f9f06 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,7 @@  Major new features:
 
 * The strlcpy and strlcat functions have been added.  They are derived
   from OpenBSD, and are expected to be added to a future POSIX versions.
+  The wide variants wcslcpy and wcslcat have been added as well.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
diff --git a/debug/Makefile b/debug/Makefile
index 404f93002f..9098d35846 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -40,7 +40,7 @@  routines  = backtrace backtracesyms backtracesymsfd noophooks \
 	    realpath_chk fread_chk fread_u_chk \
 	    wctomb_chk wcscpy_chk wmemcpy_chk wmemmove_chk wmempcpy_chk \
 	    wcpcpy_chk wcsncpy_chk wcscat_chk wcsncat_chk wmemset_chk \
-	    wcpncpy_chk \
+	    wcpncpy_chk wcslcat_chk wcslcpy_chk \
 	    swprintf_chk vswprintf_chk wprintf_chk fwprintf_chk \
 	    vwprintf_chk vfwprintf_chk fgetws_chk fgetws_u_chk \
 	    confstr_chk getgroups_chk ttyname_r_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/manual/string.texi b/manual/string.texi
index e3979f1d0f..a1ee245213 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -1097,6 +1097,16 @@  array overlap.
 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{}}
@@ -1122,6 +1132,16 @@  first @var{size} bytes of @var{to} overlap.
 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 text.  When coping or
 concatening multibyte strings, they can truncate within a multibyte
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index e195853f09..2a3ebc0ed1 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2326,8 +2326,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 b12a3b0750..bc111ca229 100644
--- a/wcsmbs/Makefile
+++ b/wcsmbs/Makefile
@@ -45,7 +45,8 @@  routines := wcscat wcschr wcscmp wcscpy wcscspn wcsdup wcslen wcsncat \
 	    isoc99_swscanf isoc99_vswscanf \
 	    isoc23_wscanf isoc23_vwscanf isoc23_fwscanf isoc23_vfwscanf \
 	    isoc23_swscanf isoc23_vswscanf \
-	    mbrtoc8 c8rtomb mbrtoc16 c16rtomb mbrtoc32 c32rtomb
+	    mbrtoc8 c8rtomb mbrtoc16 c16rtomb mbrtoc32 c32rtomb \
+	    wcslcpy wcslcat
 
 strop-tests :=  wcscmp wcsncmp wmemcmp wcslen wcschr wcsrchr wcscpy wcsnlen \
 		wcpcpy wcsncpy wcpncpy wcscat wcsncat wcschrnul wcsspn wcspbrk \
@@ -65,7 +66,10 @@  tests := tst-wcstof wcsmbs-tst1 tst-wcsnlen tst-btowc tst-mbrtowc \
 	 tst-wscanf-binary-c11 \
 	 tst-wscanf-binary-c2x \
 	 tst-wscanf-binary-gnu11 \
-	 tst-wscanf-binary-gnu89
+	 tst-wscanf-binary-gnu89 \
+	 tst-wcslcat \
+	 tst-wcslcpy \
+	 # tests
 
 include ../Rules
 
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)