public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation
@ 2023-04-05 11:20 Florian Weimer
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
                   ` (3 more replies)
  0 siblings, 4 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-05 11:20 UTC (permalink / raw)
  To: libc-alpha; +Cc: Paul Eggert

These two patches add support for four functions planned for addition to
POSIX, plus their fortified variants.  They are available with
_DEFAULT_SOURCE because the BSDs expose them by default, oto.

Tested on i686-linux-gnu and x86_64-linux-gnu.  Built with
build-many-glibcs.py (on all ABIs, but not all targets because disk
space is no longer sufficient on the lab machines I have access to).

Paul, the Austin Groups issue you raised has been closed.  Do you keep
your sustained objection to adding these functions to glibc?

Thanks,
Florian

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

 NEWS                                          |  6 ++
 debug/Makefile                                |  3 +-
 debug/Versions                                |  6 ++
 debug/strlcat_chk.c                           | 32 +++++++
 debug/strlcpy_chk.c                           | 32 +++++++
 debug/tst-fortify.c                           | 48 ++++++++++
 debug/wcslcat_chk.c                           | 31 +++++++
 debug/wcslcpy_chk.c                           | 31 +++++++
 include/string.h                              |  4 +
 include/wchar.h                               |  5 +
 manual/string.texi                            | 74 +++++++++++++++
 string/Makefile                               |  4 +
 string/Versions                               |  4 +
 string/bits/string_fortified.h                | 36 +++++++
 string/string.h                               | 13 +++
 string/strlcat.c                              | 61 ++++++++++++
 string/strlcpy.c                              | 48 ++++++++++
 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                               |  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 +++++++++
 62 files changed, 1204 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: 16439f419b270184ec501c531bf20d83b6745fb0
-- 
2.39.2


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

* [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 11:20 [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Florian Weimer
@ 2023-04-05 11:20 ` Florian Weimer
  2023-04-05 13:18   ` Adhemerval Zanella Netto
                     ` (3 more replies)
  2023-04-05 11:20 ` [PATCH 2/2] Add the wcslcpy, wcslcat functions Florian Weimer
                   ` (2 subsequent siblings)
  3 siblings, 4 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-05 11:20 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.  Maybe this
should be changed to catch secondary overflows because the string
appears longer than the buffer.
---
 NEWS                                          |  5 ++
 debug/Makefile                                |  1 +
 debug/Versions                                |  4 +
 debug/strlcat_chk.c                           | 32 +++++++
 debug/strlcpy_chk.c                           | 32 +++++++
 debug/tst-fortify.c                           | 31 +++++++
 include/string.h                              |  4 +
 manual/string.texi                            | 54 ++++++++++++
 string/Makefile                               |  4 +
 string/Versions                               |  4 +
 string/bits/string_fortified.h                | 36 ++++++++
 string/string.h                               | 13 +++
 string/strlcat.c                              | 61 ++++++++++++++
 string/strlcpy.c                              | 48 +++++++++++
 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 +
 51 files changed, 621 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..60b40fabcf 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 versions.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * In the Linux kernel for the hppa/parisc architecture some of the
@@ -223,6 +226,8 @@ Major new features:
 
   The LoongArch ABI is 64-bit little-endian.
 
+* The functions strlcpy and strlcat have been added.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Support for prelink will be removed in the next release; this includes
diff --git a/debug/Makefile b/debug/Makefile
index 52f9a7852c..404f93002f 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -31,6 +31,7 @@ headers	:= execinfo.h
 routines  = backtrace backtracesyms backtracesymsfd noophooks \
 	    memcpy_chk memmove_chk mempcpy_chk memset_chk stpcpy_chk \
 	    strcat_chk strcpy_chk strncat_chk strncpy_chk stpncpy_chk \
+	    strlcpy_chk strlcat_chk \
 	    sprintf_chk vsprintf_chk snprintf_chk vsnprintf_chk \
 	    printf_chk fprintf_chk vprintf_chk vfprintf_chk \
 	    gets_chk chk_fail readonly-area fgets_chk fgets_u_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..be6e1942af
--- /dev/null
+++ b/debug/strlcat_chk.c
@@ -0,0 +1,32 @@
+/* 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>
+#include <memcopy.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..0137886e91
--- /dev/null
+++ b/debug/strlcpy_chk.c
@@ -0,0 +1,32 @@
+/* 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>
+#include <memcopy.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/manual/string.texi b/manual/string.texi
index e06433187e..e3979f1d0f 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -1068,6 +1068,60 @@ processing text.  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 is similar to @code{strcpy}, but copies at most
+@var{size} bytes from the string @var{from} into the destination
+array @var{to}, including a terminating null byte.
+
+If @var{size} is greater than the length of the string @var{from},
+this function copies all of the string @var{from} to the destination
+array @var{to}, including the terminating 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.
+
+The return value @var{result} of @code{strlcpy} is the length of the
+string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
+true whenever truncation occurs.
+
+The behavior of @code{strlcpy} is undefined if @var{size} is zero, or if
+the source string and the first @var{size} bytes of the destination
+array overlap.
+
+This function is derived from OpenBSD 2.4.
+@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 total size of the result string at
+@var{to} (including the null terminator) to @var{size}.
+
+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 unless
+the returned value is less than @var{size}.
+
+The behavior is undefined if the array at @var{to} does not contain a
+null byte in its first @var{size} bytes, or if the source string and the
+first @var{size} bytes of @var{to} overlap.
+
+This function is derived from OpenBSD 2.4.
+@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/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 7f0f600224..fcfb3fce74 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..5b64072004
--- /dev/null
+++ b/string/strlcat.c
@@ -0,0 +1,61 @@
+/* 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>
+
+#undef strlcat
+
+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..b863a4762b
--- /dev/null
+++ b/string/strlcpy.c
@@ -0,0 +1,48 @@
+/* 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>
+
+#undef strlcpy
+
+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 da1cad6777..e195853f09 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2324,6 +2324,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.39.2



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

* [PATCH 2/2] Add the wcslcpy, wcslcat functions
  2023-04-05 11:20 [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Florian Weimer
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
@ 2023-04-05 11:20 ` Florian Weimer
  2023-04-08 22:09   ` Paul Eggert
  2023-04-05 12:30 ` [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Alejandro Colomar
  2023-04-08 22:05 ` Paul Eggert
  3 siblings, 1 reply; 28+ messages in thread
From: Florian Weimer @ 2023-04-05 11:20 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.
---
 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

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


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

* Re: [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation
  2023-04-05 11:20 [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Florian Weimer
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
  2023-04-05 11:20 ` [PATCH 2/2] Add the wcslcpy, wcslcat functions Florian Weimer
@ 2023-04-05 12:30 ` Alejandro Colomar
  2023-04-08 22:05 ` Paul Eggert
  3 siblings, 0 replies; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-05 12:30 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha; +Cc: Paul Eggert


[-- Attachment #1.1: Type: text/plain, Size: 1588 bytes --]

Hi Florian,

On 4/5/23 13:20, Florian Weimer via Libc-alpha wrote:
> These two patches add support for four functions planned for addition to
> POSIX, plus their fortified variants.  They are available with
> _DEFAULT_SOURCE because the BSDs expose them by default, oto.
> 
> Tested on i686-linux-gnu and x86_64-linux-gnu.  Built with
> build-many-glibcs.py (on all ABIs, but not all targets because disk
> space is no longer sufficient on the lab machines I have access to).
> 
> Paul, the Austin Groups issue you raised has been closed.  Do you keep
> your sustained objection to adding these functions to glibc?

Just adding a data point about this:

In shadow, we improved existing code going from strncpy(3) to strlcpy(3).
See <https://github.com/shadow-maint/shadow/pull/609>.

In some cases, as corretly pointed out by Paul, we could futher improve
some cases by just calling strcpy(3).  However, in some others,
strlcpy(3) was the sanest API, and while we could check prior to
strcpy(3)/memcpy(3) with strlen(3), that was unnecessarily cluttering
the code.  See <https://github.com/shadow-maint/shadow/pull/681>.

So I like the idea of adding this API to glibc.  In fact, it would
reduce the need for libbsd in some packages like shadow (although we
still need it for readpassphrase(3)).

Cheers,
Alex

> 
> Thanks,
> Florian
> 
> Florian Weimer (2):
>   Implement strlcpy and strlcat [BZ #178]
>   Add the wcslcpy, wcslcat functions
>
-- 
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
@ 2023-04-05 13:18   ` Adhemerval Zanella Netto
  2023-04-06  9:18     ` Florian Weimer
  2023-04-06 14:22   ` Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 28+ messages in thread
From: Adhemerval Zanella Netto @ 2023-04-05 13:18 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 05/04/23 08:20, 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.  Maybe this
> should be changed to catch secondary overflows because the string
> appears longer than the buffer.

This is not a full review, just nits I found skimming the patch.

> diff --git a/NEWS b/NEWS
> index 83d082afad..60b40fabcf 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 versions.
> +
>  Deprecated and removed features, and other changes affecting compatibility:
>  
>  * In the Linux kernel for the hppa/parisc architecture some of the
> @@ -223,6 +226,8 @@ Major new features:
>  
>    The LoongArch ABI is 64-bit little-endian.
>  
> +* The functions strlcpy and strlcat have been added.

This NEWS entry seems to be a left-over from refactoring.

> +
>  Deprecated and removed features, and other changes affecting compatibility:
>  
>  * Support for prelink will be removed in the next release; this includes
> diff --git a/debug/Makefile b/debug/Makefile
> index 52f9a7852c..404f93002f 100644
> --- a/debug/Makefile
> +++ b/debug/Makefile
> @@ -31,6 +31,7 @@ headers	:= execinfo.h
>  routines  = backtrace backtracesyms backtracesymsfd noophooks \
>  	    memcpy_chk memmove_chk mempcpy_chk memset_chk stpcpy_chk \
>  	    strcat_chk strcpy_chk strncat_chk strncpy_chk stpncpy_chk \
> +	    strlcpy_chk strlcat_chk \
>  	    sprintf_chk vsprintf_chk snprintf_chk vsnprintf_chk \
>  	    printf_chk fprintf_chk vprintf_chk vfprintf_chk \
>  	    gets_chk chk_fail readonly-area fgets_chk fgets_u_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..be6e1942af
> --- /dev/null
> +++ b/debug/strlcat_chk.c
> @@ -0,0 +1,32 @@
> +/* 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>
> +#include <memcopy.h>

It does not require this header.

> +
> +/* 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..0137886e91
> --- /dev/null
> +++ b/debug/strlcpy_chk.c
> @@ -0,0 +1,32 @@
> +/* 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>
> +#include <memcopy.h>

Ditto.

> diff --git a/string/strlcat.c b/string/strlcat.c
> new file mode 100644
> index 0000000000..5b64072004
> --- /dev/null
> +++ b/string/strlcat.c
> @@ -0,0 +1,61 @@
> +/* 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>
> +
> +#undef strlcat
> +
> +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");

Ins't garanteed by POSIX?

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 13:18   ` Adhemerval Zanella Netto
@ 2023-04-06  9:18     ` Florian Weimer
  0 siblings, 0 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-06  9:18 UTC (permalink / raw)
  To: Adhemerval Zanella Netto; +Cc: libc-alpha

* Adhemerval Zanella Netto:

> On 05/04/23 08:20, 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.  Maybe this
>> should be changed to catch secondary overflows because the string
>> appears longer than the buffer.
>
> This is not a full review, just nits I found skimming the patch.

I applied your suggestions locally.

>> +  /* 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");
>
> Ins't garanteed by POSIX?

Sure, but I felt it added additional documentation.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
  2023-04-05 13:18   ` Adhemerval Zanella Netto
@ 2023-04-06 14:22   ` Siddhesh Poyarekar
  2023-04-06 15:09     ` Florian Weimer
                       ` (2 more replies)
  2023-04-06 21:21   ` Alejandro Colomar
  2023-04-08 22:08   ` Paul Eggert
  3 siblings, 3 replies; 28+ messages in thread
From: Siddhesh Poyarekar @ 2023-04-06 14:22 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha



On 2023-04-05 07:20, 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.  Maybe this
> should be changed to catch secondary overflows because the string
> appears longer than the buffer.
> ---
>   NEWS                                          |  5 ++
>   debug/Makefile                                |  1 +
>   debug/Versions                                |  4 +
>   debug/strlcat_chk.c                           | 32 +++++++
>   debug/strlcpy_chk.c                           | 32 +++++++
>   debug/tst-fortify.c                           | 31 +++++++
>   include/string.h                              |  4 +
>   manual/string.texi                            | 54 ++++++++++++
>   string/Makefile                               |  4 +
>   string/Versions                               |  4 +
>   string/bits/string_fortified.h                | 36 ++++++++
>   string/string.h                               | 13 +++
>   string/strlcat.c                              | 61 ++++++++++++++
>   string/strlcpy.c                              | 48 +++++++++++
>   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 +
>   51 files changed, 621 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..60b40fabcf 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 versions.
> +
>   Deprecated and removed features, and other changes affecting compatibility:
>   
>   * In the Linux kernel for the hppa/parisc architecture some of the
> @@ -223,6 +226,8 @@ Major new features:
>   
>     The LoongArch ABI is 64-bit little-endian.
>   
> +* The functions strlcpy and strlcat have been added.
> +
>   Deprecated and removed features, and other changes affecting compatibility:
>   
>   * Support for prelink will be removed in the next release; this includes
> diff --git a/debug/Makefile b/debug/Makefile
> index 52f9a7852c..404f93002f 100644
> --- a/debug/Makefile
> +++ b/debug/Makefile
> @@ -31,6 +31,7 @@ headers	:= execinfo.h
>   routines  = backtrace backtracesyms backtracesymsfd noophooks \
>   	    memcpy_chk memmove_chk mempcpy_chk memset_chk stpcpy_chk \
>   	    strcat_chk strcpy_chk strncat_chk strncpy_chk stpncpy_chk \
> +	    strlcpy_chk strlcat_chk \

Do you want to commit an obvious change first to update this routine 
list to be one per line?

>   	    sprintf_chk vsprintf_chk snprintf_chk vsnprintf_chk \
>   	    printf_chk fprintf_chk vprintf_chk vfprintf_chk \
>   	    gets_chk chk_fail readonly-area fgets_chk fgets_u_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..be6e1942af
> --- /dev/null
> +++ b/debug/strlcat_chk.c
> @@ -0,0 +1,32 @@
> +/* 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>
> +#include <memcopy.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);
> +}

OK.

> diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
> new file mode 100644
> index 0000000000..0137886e91
> --- /dev/null
> +++ b/debug/strlcpy_chk.c
> @@ -0,0 +1,32 @@
> +/* 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>
> +#include <memcopy.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
>   
>   
> 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)

Do we want to delay doing this until we have an actual internal use of 
these interfaces?

> diff --git a/manual/string.texi b/manual/string.texi
> index e06433187e..e3979f1d0f 100644
> --- a/manual/string.texi
> +++ b/manual/string.texi
> @@ -1068,6 +1068,60 @@ processing text.  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}

Should we also mention POSIX here?

> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function is similar to @code{strcpy}, but copies at most
> +@var{size} bytes from the string @var{from} into the destination
> +array @var{to}, including a terminating null byte.
> +
> +If @var{size} is greater than the length of the string @var{from},
> +this function copies all of the string @var{from} to the destination
> +array @var{to}, including the terminating 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.
> +
> +The return value @var{result} of @code{strlcpy} is the length of the
> +string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
> +true whenever truncation occurs.
> +
> +The behavior of @code{strlcpy} is undefined if @var{size} is zero, or if
> +the source string and the first @var{size} bytes of the destination
> +array overlap. > +
> +This function is derived from OpenBSD 2.4.
> +@end deftypefun
> +
> +@deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
> +@standards{BSD, string.h}

Likewise.

> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function appends the string @var{from} to the
> +string @var{to}, limiting the total size of the result string at
> +@var{to} (including the null terminator) to @var{size}.
> +
> +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 unless
> +the returned value is less than @var{size}.
> +
> +The behavior is undefined if the array at @var{to} does not contain a
> +null byte in its first @var{size} bytes, or if the source string and the
> +first @var{size} bytes of @var{to} overlap. > +This function is derived from OpenBSD 2.4.
> +@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/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 */
> +

Couldn't we use the __glibc_fortify macros here?

>   #endif /* bits/string_fortified.h */
> diff --git a/string/string.h b/string/string.h
> index 7f0f600224..fcfb3fce74 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..5b64072004
> --- /dev/null
> +++ b/string/strlcat.c
> @@ -0,0 +1,61 @@
> +/* 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>
> +
> +#undef strlcat
> +
> +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;
> +}

OK.

> +libc_hidden_def (__strlcat)
> +weak_alias (__strlcat, strlcat)
> diff --git a/string/strlcpy.c b/string/strlcpy.c
> new file mode 100644
> index 0000000000..b863a4762b
> --- /dev/null
> +++ b/string/strlcpy.c
> @@ -0,0 +1,48 @@
> +/* 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>
> +
> +#undef strlcpy
> +
> +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);

size == 0 is undefined anyway; we return without touching the dest 
because that's convenient for us.  OK.

> +  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 da1cad6777..e195853f09 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2324,6 +2324,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] 28+ messages in thread

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 14:22   ` Siddhesh Poyarekar
@ 2023-04-06 15:09     ` Florian Weimer
  2023-04-06 21:29     ` Alejandro Colomar
  2023-04-20 10:55     ` Florian Weimer
  2 siblings, 0 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-06 15:09 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

* Siddhesh Poyarekar:

> On 2023-04-05 07:20, Florian Weimer via Libc-alpha wrote:
>> --- a/debug/Makefile
>> +++ b/debug/Makefile
>> @@ -31,6 +31,7 @@ headers	:= execinfo.h
>>   routines  = backtrace backtracesyms backtracesymsfd noophooks \
>>   	    memcpy_chk memmove_chk mempcpy_chk memset_chk stpcpy_chk \
>>   	    strcat_chk strcpy_chk strncat_chk strncpy_chk stpncpy_chk \
>> +	    strlcpy_chk strlcat_chk \
>
> Do you want to commit an obvious change first to update this routine
> list to be one per line?

I supoose I could do that, also for string and wcsmbs.

>> diff --git a/manual/string.texi b/manual/string.texi
>> index e06433187e..e3979f1d0f 100644
>> --- a/manual/string.texi
>> +++ b/manual/string.texi
>> @@ -1068,6 +1068,60 @@ processing text.  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}
>
> Should we also mention POSIX here?

It would be misleading because it's not possible to enable this using
_POSIX_SOURCE, I think.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
  2023-04-05 13:18   ` Adhemerval Zanella Netto
  2023-04-06 14:22   ` Siddhesh Poyarekar
@ 2023-04-06 21:21   ` Alejandro Colomar
  2023-04-06 21:35     ` Florian Weimer
  2023-04-06 22:34     ` Alejandro Colomar
  2023-04-08 22:08   ` Paul Eggert
  3 siblings, 2 replies; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-06 21:21 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha

Hi Florian,

On 4/5/23 13:20, 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.  Maybe this
> should be changed to catch secondary overflows because the string
> appears longer than the buffer.
> ---

[...]

> +size_t
> +__strlcat (char *__restrict dest, const char *__restrict src, size_t size)
> +{

strlcat(3) is a slow call that shouldn't be emitted by an optimizing
compiler (similar to how GCC optimizes strcat(3) to stpcpy(3)).

With that in mind, do we really want all this code?  Or maybe we just
want to implement it in the simplest possible way?

I suggest the following alternative:


size_t
strlcat(char *restrict dst, const char *restrict src, size_t dsize)
{
    size_t  dlen = strlen(dst);

    return strlcpy(dst + dlen, src, dsize - dlen) + dlen;
}


In fact, that's the code I would expect that GCC should emit instead
of calls to the actual strlcat(3).

(Disclaimer: I didn't test the code; it may likely have bugs)

> +  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);

The OpenBSD contract of strlcat(3) includes that _both_ the source
string and the destination strings are NULL-terminated.  I guess
POSIX has kept that contract.  If that's the case, we can just call
strlen(3) here.

> +  if (dest_length != size)

This should always happen.  Else, it is undefined, because it means
that the destination string wasn't terminated.

Relevant quote of BSD's man page:

[
                               ...  Also note  that  strlcpy()  and  strlcat()
       only  operate  on  true “C” strings.  This means that ...
                              ... for strlcat() both src and dst must be  NUL‐
       terminated.
]

So the conditional can be omitted.


> +    {
> +      /* 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)

[...]

> +
> +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;
> +}

L(very)GTM. :)


Cheers,
Alex


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 14:22   ` Siddhesh Poyarekar
  2023-04-06 15:09     ` Florian Weimer
@ 2023-04-06 21:29     ` Alejandro Colomar
  2023-04-11 14:28       ` Siddhesh Poyarekar
  2023-04-20 10:55     ` Florian Weimer
  2 siblings, 1 reply; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-06 21:29 UTC (permalink / raw)
  To: Siddhesh Poyarekar, Florian Weimer, libc-alpha

Hi Siddhesh,

On 4/6/23 16:22, Siddhesh Poyarekar wrote:
[...]

>> +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);
> 
> size == 0 is undefined anyway; we return without touching the dest 
> because that's convenient for us.  OK.

Is it really convenient?  What real code benefits of that behavior?
If we remove the conditional it's one less op.

Cheers,

Alex

> 
>> +  return src_length;
>> +}

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 21:21   ` Alejandro Colomar
@ 2023-04-06 21:35     ` Florian Weimer
  2023-04-06 22:15       ` Alejandro Colomar
  2023-04-06 22:19       ` Alejandro Colomar
  2023-04-06 22:34     ` Alejandro Colomar
  1 sibling, 2 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-06 21:35 UTC (permalink / raw)
  To: Alejandro Colomar via Libc-alpha; +Cc: Alejandro Colomar

* Alejandro Colomar via Libc-alpha:

>> +  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);
>
> The OpenBSD contract of strlcat(3) includes that _both_ the source
> string and the destination strings are NULL-terminated.  I guess
> POSIX has kept that contract.  If that's the case, we can just call
> strlen(3) here.

NetBSD says this:

| Note however, that if strlcat() traverses size characters without
| finding a NUL, the length of the string is considered to be size and
| the destination string will not be NUL-terminated (since there was
| no space for the NUL).  This keeps strlcat() from running off the
| end of a string.  In practice this should not happen (as it means
| that either size is incorrect or that dst is not a proper ``C''
| string).  The check exists to prevent potential security problems in
| incorrect code.

<https://man.netbsd.org/strlcat.3>

OpenBSD alludes to this as well:

| strlcat() appends string src to the end of dst. It will append at
| most dstsize - strlen(dst) - 1 characters. It will then
| NUL-terminate, unless dstsize is 0 or the original dst string was
| longer than dstsize (in practice this should not happen as it means
| that either dstsize is incorrect or that dst is not a proper
| string).

<https://man.openbsd.org/strlcat>

So I think we should be calling strnlen here.  If we call strlen
instead, we'd have to bound the result.

Thanks,
Florian

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 21:35     ` Florian Weimer
@ 2023-04-06 22:15       ` Alejandro Colomar
  2023-04-06 22:19       ` Alejandro Colomar
  1 sibling, 0 replies; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-06 22:15 UTC (permalink / raw)
  To: Florian Weimer, Alejandro Colomar via Libc-alpha


[-- Attachment #1.1: Type: text/plain, Size: 1887 bytes --]

Hi Florian,

On 4/6/23 23:35, Florian Weimer wrote:
[...]

> 
> OpenBSD alludes to this as well:
> 
> | strlcat() appends string src to the end of dst. It will append at
> | most dstsize - strlen(dst) - 1 characters. It will then
> | NUL-terminate, unless dstsize is 0 or the original dst string was
> | longer than dstsize (in practice this should not happen as it means
> | that either dstsize is incorrect or that dst is not a proper
> | string).

Hmm, that text replaced the one I quoted (which has been kept in
libbsd's page), in this commit:


commit 5df0f979487d1950d0e8885be683158e55ff4ae3
Author: deraadt <deraadt@openbsd.org>
Date:   Mon Apr 2 17:33:11 2012 +0000

    simplify the strlcpy/strlcat manual page substantially.  do less
    explaining of "what a C string is", and make it more clear that these
    functiosn BEHAVE EXACTLY LIKE snprintf with "%s"!  (anyone who wants
    to write a 'strlcpy considered harmful' paper should probably write a
    'strlcpy and snprintf considered harmful' paper instead).
    
    note to those from other projects reading this commit message: It would
    be very good if this new manual was picked up in your project.
    
    ok jmc millert krw

> 
> <https://man.openbsd.org/strlcat>
> 
> So I think we should be calling strnlen here.  If we call strlen
> instead, we'd have to bound the result.

Hmm, it depends on what we decide to do with the conditional right
after the strnlen(3) call.  Either we remove the conditional, or
transform strnlen(3) to strlen(3), but not both.  I guess strnlen(3)
is more readable, since it's just one line.

Anyway, as I said, I would just burn it all and make this function
be a two-liner.  :)

Cheers,
Alex

> 
> Thanks,
> Florian

-- 
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 21:35     ` Florian Weimer
  2023-04-06 22:15       ` Alejandro Colomar
@ 2023-04-06 22:19       ` Alejandro Colomar
  1 sibling, 0 replies; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-06 22:19 UTC (permalink / raw)
  To: Florian Weimer, Alejandro Colomar via Libc-alpha


[-- Attachment #1.1: Type: text/plain, Size: 1726 bytes --]

On 4/6/23 23:35, Florian Weimer wrote:
> NetBSD says this:
> 
> | Note however, that if strlcat() traverses size characters without
> | finding a NUL, the length of the string is considered to be size and
> | the destination string will not be NUL-terminated (since there was
> | no space for the NUL).  This keeps strlcat() from running off the
> | end of a string.  In practice this should not happen (as it means
> | that either size is incorrect or that dst is not a proper ``C''
> | string).  The check exists to prevent potential security problems in
> | incorrect code.
> 
> <https://man.netbsd.org/strlcat.3>
> 
> OpenBSD alludes to this as well:
> 
> | strlcat() appends string src to the end of dst. It will append at
> | most dstsize - strlen(dst) - 1 characters. It will then
> | NUL-terminate, unless dstsize is 0 or the original dst string was
> | longer than dstsize (in practice this should not happen as it means
> | that either dstsize is incorrect or that dst is not a proper
> | string).
> 
> <https://man.openbsd.org/strlcat>
> 
> So I think we should be calling strnlen here.  If we call strlen
> instead, we'd have to bound the result.

AFAIR, the design behind strlcpy(3) and cat(3) was that they would
intentionally overrun the buffers (read-only) to force crashes as
much as possible, which would uncover bugs in the code, rather than
silently continuing.  Don't know why they changed that.  Since it's
just reading the string without writing to it, I don't think anything
worse than a crash could possibly happen.

Cheers,
Alex

> 
> Thanks,
> Florian

-- 
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 21:21   ` Alejandro Colomar
  2023-04-06 21:35     ` Florian Weimer
@ 2023-04-06 22:34     ` Alejandro Colomar
  1 sibling, 0 replies; 28+ messages in thread
From: Alejandro Colomar @ 2023-04-06 22:34 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha


[-- Attachment #1.1: Type: text/plain, Size: 1016 bytes --]

On 4/6/23 23:21, Alejandro Colomar wrote:
> strlcat(3) is a slow call that shouldn't be emitted by an optimizing
> compiler (similar to how GCC optimizes strcat(3) to stpcpy(3)).
> 
> With that in mind, do we really want all this code?  Or maybe we just
> want to implement it in the simplest possible way?
> 
> I suggest the following alternative:
> 
> 
> size_t
> strlcat(char *restrict dst, const char *restrict src, size_t dsize)
> {
>     size_t  dlen = strlen(dst);

This needs to be strnlen(dst, dsize), to avoid passing a huge size to
strlcpy(3).  This seems a good reason for having defined behavior for
strlcpy(x, y, 0).

> 
>     return strlcpy(dst + dlen, src, dsize - dlen) + dlen;
> }
> 
> 
> In fact, that's the code I would expect that GCC should emit instead
> of calls to the actual strlcat(3).
> 
> (Disclaimer: I didn't test the code; it may likely have bugs)
> 
-- 
<http://www.alejandro-colomar.es/>
GPG key fingerprint: A9348594CE31283A826FBDD8D57633D441E25BB5

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation
  2023-04-05 11:20 [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Florian Weimer
                   ` (2 preceding siblings ...)
  2023-04-05 12:30 ` [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Alejandro Colomar
@ 2023-04-08 22:05 ` Paul Eggert
  3 siblings, 0 replies; 28+ messages in thread
From: Paul Eggert @ 2023-04-08 22:05 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On 4/5/23 04:20, Florian Weimer wrote:
> Paul, the Austin Groups issue you raised has been closed.  Do you keep
> your sustained objection to adding these functions to glibc?

Yes, I'll withdraw that objection if we add the functions in a good way. 
Compatibility with POSIX is more important than omitting a misguided 
API, so long as the harm is limited to people who insist on using the 
API despite being warned.

By "good way" I mean the following three things.

1. Fix the manual to agree with draft POSIX (currently it disagrees). 
Also, the manual should mention the same sort of problems for 
strlcpy/etc. that it already mentions for strncpy/etc. - truncation can 
lead to security problems, non-text strings, that sort of thing.

2. Do not implement these functions via memcpy or strnlen or anything 
like that. Just use simple code like OpenBSD's. Preferably just use 
OpenBSD code as-is. Otherwise we have the danger of straying from 
OpenBSD semantics. This danger is already present in the proposed 
patches, which have undefined behavior in rare cases where OpenBSD's 
behavior is well-defined and where draft POSIX specifies the OpenBSD 
behavior.

We don't have time to formally verify or otherwise carefully audit this 
code, so this is no place to bikeshed or optimize. The goal is 
compatibility, not speed. Besides, strlcpy destinations and sources are 
almost always small, so in practice using memcpy etc. will likely be 
slower than just doing things the OpenBSD way.

3. The patch should be more conservative: it should declare these 
functions in string.h only on user request, i.e., only when compiled 
with -D_XOPEN_SOURCE=800 (or -D_POSIX_C_SOURCE=2023mmL) without 
-D_GNU_SOURCE. (We can add a new _STRLCPY_SOURCE macro too if that would 
be helpful, to selectively make these new functions visible.) This will 
suffice for POSIX conformance, and it will help avoid bugs when existing 
applications that already supply their own slightly-different strlcpy 
substitutes are compiled with new glibc: it will give these apps' 
maintainers the ability to decide after their own manual inspection 
whether and when to switch to glibc's strlcpy.

I'll follow up with more detailed comments on individual patches. And 
I'll volunteer to do (1), which is likely the most work anyway. I've 
already done much of (1) and will attach that to my detailed comments.

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
                     ` (2 preceding siblings ...)
  2023-04-06 21:21   ` Alejandro Colomar
@ 2023-04-08 22:08   ` Paul Eggert
  2023-04-09 15:29     ` Paul Eggert
  2023-04-20  8:07     ` Florian Weimer
  3 siblings, 2 replies; 28+ messages in thread
From: Paul Eggert @ 2023-04-08 22:08 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

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

On 2023-04-05 04:20, Florian Weimer via Libc-alpha wrote:

> 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.  Maybe this
> should be changed to catch secondary overflows because the string
> appears longer than the buffer.

Shouldn't it should work like fortified strcat, which raises SIGABRT if 
the destination is not null-terminated? (Admittedly this isn't important.)


> +* The strlcpy and strlcat functions have been added.  They are derived
> +  from OpenBSD, and are expected to be added to a future POSIX versions.

versions → version

Please see the attached patch for a fix for this.


> +* The functions strlcpy and strlcat have been added.

That's a duplicate in NEWS.


> +extern __typeof (strlcpy) __strlcpy;
> +libc_hidden_proto (__strlcpy)
> +extern __typeof (strlcat) __strlcat;
> +libc_hidden_proto (__strlcat)

Glibc shouldn't call these functions internally, so let's not export 
them to elsewhere in glibc.

manual/maint.texi needs changing too (see where it discusses strncpy).


> --- a/manual/string.texi
> +++ b/manual/string.texi

When the manual discusses strcat/strncat's O(N**2) drawback it should do 
the same for strlcat.


> +This function is similar to @code{strcpy}, but copies at most
> +@var{size} bytes from the string @var{from} into the destination
> +array @var{to}, including a terminating null byte.

This should mention draft POSIX's recommendation that the caller should 
ensure that SIZE has room for the terminating null. Also, it shouldn't 
say it copies at most SIZE bytes because it's really at most (MAX (1, 
SIZE) - 1). There are several errors of this sort later on - for 
example, the overlapping move constraint doesn't apply to the null 
terminator. I think I caught the errors in the attached patch but 
another pair of eyes wouldn't hurt.

Also, let's use similar wording for strlcpy and strlcat to avoid having 
the readers think that there are differences when there aren't any.


> +The return value @var{result} of @code{strlcpy} is the length of the
> +string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
> +true whenever truncation occurs.

It's not just "whenever" (i.e., if); it's if and only if.


> +The behavior of @code{strlcpy} is undefined if @var{size} is zero, or if
> +the source string and the first @var{size} bytes of the destination
> +array overlap.

Actually, the behavior is well-defined if SIZE is zero. It's also 
well-defined in some cases when the first SIZE bytes overlap: the only 
constraint is that the region copied from cannot overlap with the region 
copied to (in both cases, not counting any terminating null). However, 
behavior is undefined if either pointer is null, or if the destination 
array isn't big enough.

The description should allude to the truncation-warning discussion below.

There are similar issues for the strlcat doc, which the attached patch 
also attempts to fix.


> +  /* 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");

Change the "==" to "<=" (since that's what is meant here) and clarify 
the comment which is a bit confusing. Better yet, let's omit this and 
all related code and go with the OpenBSD implementation.


> +	  /* 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';

This micro-optimization is incorrect, as it's valid for dest to equal 
src + size - 1, and that means the memcpy overlaps which is undefined. 
Change it to memcpy (dest, src, size - 1) and lose the comment. Or 
change it to memmove and lengthen the comment. Or better yet, get rid of 
all code like this (there are other instances), and use the simple 
OpenBSD implementation which will more likely match what callers expect 
(in the rare cases where the behaviors differ) and will possibly be 
faster despite not using memcpy.


Proposed patch for NEWS and manual attached; the other problems remain 
unfixed.

[-- Attachment #2: 0001-manual-improve-strlcpy-strlcat-doc.patch --]
[-- Type: text/x-patch, Size: 8392 bytes --]

From edd58081dffcbd83f8f89a7acb9f323653bd7a56 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 8 Apr 2023 13:54:37 -0700
Subject: [PATCH] manual: improve strlcpy/strlcat doc

* NEWS: Remove duplicate announcement of strlcpy/strlcat.
* manual/maint.texi (Source Fortification):
Mention strlcpy and strlcat.
* manual/string.texi (Truncating Strings): In strlcpy/strlcat,
mention that the caller should ensure room for the terminating
null byte, as draft POSIX does.  Don't say that the behavior
is undefined merely because the size is zero, since it's well defined.
Accurately describe the prohibition against overlapping copies.
Use the same language about string-processing choice
and about performance that we already use for strncpy and strncat.
Avoid unnecessarily-different wording between strlcpy and strlcat
that might cause the reader to mistakenly think of other differences.
Mention that strlcpy is O(max(size, strlen(src))), not O(size).
---
 NEWS               |  4 +--
 manual/maint.texi  |  4 +++
 manual/string.texi | 65 +++++++++++++++++++++++++++++++---------------
 3 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/NEWS b/NEWS
index 60b40fabcf..b21c4c10aa 100644
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,7 @@ 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 versions.
+  from OpenBSD, and are expected to be added to a future POSIX version.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
@@ -226,8 +226,6 @@ Major new features:
 
   The LoongArch ABI is 64-bit little-endian.
 
-* The functions strlcpy and strlcat have been added.
-
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Support for prelink will be removed in the next release; this includes
diff --git a/manual/maint.texi b/manual/maint.texi
index a8441e20b6..3ad4647cf3 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}
diff --git a/manual/string.texi b/manual/string.texi
index 57e3f6a619..78cdc012d0 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}.
 
@@ -1079,13 +1080,15 @@ issues.  @xref{Concatenating Strings}.
 @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 is similar to @code{strcpy}, but copies at most
-@var{size} bytes from the string @var{from} into the destination
-array @var{to}, including a terminating null byte.
+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 all of the string @var{from} to the destination
-array @var{to}, including the terminating null byte.  Like other
+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.
 
@@ -1094,13 +1097,22 @@ If @var{size} is nonzero and less than or equal to the the length of the string
 bytes to the destination array @var{to}, and writes a terminating null
 byte to the last byte of the array.
 
-The return value @var{result} of @code{strlcpy} is the length of the
-string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
-true whenever truncation occurs.
+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 of @code{strlcpy} is undefined if @var{size} is zero, or if
-the source string and the first @var{size} bytes of the destination
-array overlap.
+The behavior is undefined if @var{to} or @var{from} is a null pointer,
+or if the destination array's size is both less than @var{size} and
+less than or equal to the length of the string @var{from}, or if
+copying takes place between arrays that overlap (that is, if
+@var{size} is non-zero and the first @samp{MIN (strlen (@var{from}),
+@var{size} - 1)} bytes of @var{from} overlap with the same number of
+bytes of the 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
@@ -1109,8 +1121,9 @@ This function is derived from OpenBSD 2.4.
 @standards{BSD, string.h}
 @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 This function appends the string @var{from} to the
-string @var{to}, limiting the total size of the result string at
-@var{to} (including the null terminator) to @var{size}.
+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
@@ -1120,12 +1133,22 @@ 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 unless
-the returned value is less than @var{size}.
+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 does not contain a null byte in its first
+@var{size} bytes, or if the destination array's size is both less than
+@var{size} and less than or equal to the sum of the lengths of the
+strings @var{to} and @var{from}, or if copying takes place between
+arrays that overlap (that is, if @var{size} is non-zero and the
+first @samp{MIN (strlen (@var{from}), @var{size} - 1 - strlen (@var{to}))}
+bytes of @var{from} overlap with the same number of bytes starting
+at @samp{@var{to} + strlen (@var{to})}.
 
-The behavior is undefined if the array at @var{to} does not contain a
-null byte in its first @var{size} bytes, or if the source string and the
-first @var{size} bytes of @var{to} overlap.
+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
-- 
2.39.2


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

* Re: [PATCH 2/2] Add the wcslcpy, wcslcat functions
  2023-04-05 11:20 ` [PATCH 2/2] Add the wcslcpy, wcslcat functions Florian Weimer
@ 2023-04-08 22:09   ` Paul Eggert
  0 siblings, 0 replies; 28+ messages in thread
From: Paul Eggert @ 2023-04-08 22:09 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

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

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.

[-- Attachment #2: 0001-manual-improve-wcslcpy-wcslcat-doc.patch --]
[-- Type: text/x-patch, Size: 3055 bytes --]

From 611f53a510743e74af9c694251c058e5b7507b6f Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 8 Apr 2023 13:54:37 -0700
Subject: [PATCH] manual: improve wcslcpy/wcslcat doc

Mention OpenBSD versions, and mention the new
functions elsewhere when appropriate.
---
 manual/maint.texi  |  4 ++++
 manual/string.texi | 13 +++++++------
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/manual/maint.texi b/manual/maint.texi
index 3ad4647cf3..89da704f45 100644
--- a/manual/maint.texi
+++ b/manual/maint.texi
@@ -415,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 781fba245a..d2c3b6c902 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -727,7 +727,8 @@ As noted below, this function has significant performance issues.
 @end deftypefun
 
 Programmers using the @code{strcat} or @code{wcscat} functions (or the
-@code{strlcat}, @code{strncat} and @code{wcsncat} functions defined in
+@code{strlcat}, @code{strncat}, @code{wcslcat}
+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
@@ -849,7 +850,7 @@ 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{strlcat}, @code{strncat},
-@code{wcscat} and @code{wcsncat}
+@code{wcscat}, @code{wcslcat} and @code{wcsncat}
 are almost always unnecessary, too.
 Again: it is almost always unnecessary to use functions like @code{strcat}.
 
@@ -1121,10 +1122,10 @@ This function is derived from OpenBSD 2.4.
 @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
+The @var{size} argument counts the length of the destination buffer in
 wide characters (and not bytes).
 
-This function is derived from BSD.
+This function is derived from OpenBSD 3.8.
 @end deftypefun
 
 @deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
@@ -1167,10 +1168,10 @@ This function is derived from OpenBSD 2.4.
 @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
+The @var{size} argument counts the length of the destination buffer in
 wide characters (and not bytes).
 
-This function is derived from BSD.
+This function is derived from OpenBSD 3.8.
 @end deftypefun
 
 Because these functions can abruptly truncate strings or wide strings,
-- 
2.39.2


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-08 22:08   ` Paul Eggert
@ 2023-04-09 15:29     ` Paul Eggert
  2023-04-13 11:37       ` Florian Weimer
  2023-04-20  8:07     ` Florian Weimer
  1 sibling, 1 reply; 28+ messages in thread
From: Paul Eggert @ 2023-04-09 15:29 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

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

On 2023-04-08 15:08, Paul Eggert wrote:

> the overlapping move constraint doesn't apply to the null 
> terminator.

Oh, sorry, scratch that: it does apply, because of the 'restrict' 
constraints on the argument pointers. Hence (contrary to my previous 
assertion) PATCH 1/2's code does conform to draft POSIX.

However, it's still worrisome that PATCH 1/2's behavior disagrees with 
OpenBSD's for some nonconforming calls. If the main point of the patch 
is compatibility, surely it is better to be compatible with existing 
practice, not merely with draft POSIX.

Anyway, please ignore the documentation patch I sent yesterday in this 
thread, and look instead at the attached. This is the same patch, except 
with fixed wording under strlcpy and strlcat as to whether behavior is 
undefined.

[-- Attachment #2: 0001-manual-improve-strlcpy-strlcat-doc.patch --]
[-- Type: text/x-patch, Size: 8356 bytes --]

From 9b5eee6f05d0728c0cf026b6c5c276855fdfc637 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 8 Apr 2023 13:54:37 -0700
Subject: [PATCH 1/2 COMMENT v2] manual: improve strlcpy/strlcat doc

* NEWS: Remove duplicate announcement of strlcpy/strlcat.
* manual/maint.texi (Source Fortification):
Mention strlcpy and strlcat.
* manual/string.texi (Truncating Strings): In strlcpy/strlcat,
mention that the caller should ensure room for the terminating
null byte, as draft POSIX does.  Don't say that the behavior
is undefined merely because the size is zero, since it's well defined.
Accurately describe the constraints imposed by 'restrict',
which are stricter than the constraints imposed by not allowing
overlapping copies.
Use the same language about string-processing choice
and about performance that we already use for strncpy and strncat.
Avoid unnecessarily-different wording between strlcpy and strlcat
that might cause the reader to mistakenly think of other differences.
Mention that strlcpy is O(max(size, strlen(src))), not O(size).
---
 NEWS               |  4 +--
 manual/maint.texi  |  4 +++
 manual/string.texi | 63 ++++++++++++++++++++++++++++++----------------
 3 files changed, 47 insertions(+), 24 deletions(-)

diff --git a/NEWS b/NEWS
index 60b40fabcf..b21c4c10aa 100644
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,7 @@ 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 versions.
+  from OpenBSD, and are expected to be added to a future POSIX version.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
@@ -226,8 +226,6 @@ Major new features:
 
   The LoongArch ABI is 64-bit little-endian.
 
-* The functions strlcpy and strlcat have been added.
-
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Support for prelink will be removed in the next release; this includes
diff --git a/manual/maint.texi b/manual/maint.texi
index a8441e20b6..3ad4647cf3 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}
diff --git a/manual/string.texi b/manual/string.texi
index 57e3f6a619..31c98dfcb3 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}.
 
@@ -1079,13 +1080,15 @@ issues.  @xref{Concatenating Strings}.
 @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 is similar to @code{strcpy}, but copies at most
-@var{size} bytes from the string @var{from} into the destination
-array @var{to}, including a terminating null byte.
+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 all of the string @var{from} to the destination
-array @var{to}, including the terminating null byte.  Like other
+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.
 
@@ -1094,13 +1097,21 @@ If @var{size} is nonzero and less than or equal to the the length of the string
 bytes to the destination array @var{to}, and writes a terminating null
 byte to the last byte of the array.
 
-The return value @var{result} of @code{strlcpy} is the length of the
-string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
-true whenever truncation occurs.
+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 of @code{strlcpy} is undefined if @var{size} is zero, or if
-the source string and the first @var{size} bytes of the destination
-array overlap.
+The behavior is undefined if @var{to} or @var{from} is a null pointer,
+or if the destination array's size is both less than @var{size} and
+less than or equal to the length of the string @var{from}, or if
+the string @var{from} overlaps the result (that is, if @var{from}
+overlaps the first @samp{MIN (@var{size}, strlen (@var{from}) + 1)}
+bytes of the the destination array @var{to}).
+
+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
@@ -1109,8 +1120,9 @@ This function is derived from OpenBSD 2.4.
 @standards{BSD, string.h}
 @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 This function appends the string @var{from} to the
-string @var{to}, limiting the total size of the result string at
-@var{to} (including the null terminator) to @var{size}.
+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
@@ -1120,12 +1132,21 @@ 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 unless
-the returned value is less than @var{size}.
+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 does not contain a null byte in its first
+@var{size} bytes, or if the destination array's size is both less than
+@var{size} and less than or equal to the sum of the lengths of the
+strings @var{from} and @var{to}, or if the string @var{from} overlaps
+the total result (that is, if @var{from} overlaps the first @samp{MIN
+(@var{size}, strlen (@var{from}) + strlen (@var{to}) + 1)} bytes of
+the destination array @var{to}).
 
-The behavior is undefined if the array at @var{to} does not contain a
-null byte in its first @var{size} bytes, or if the source string and the
-first @var{size} bytes of @var{to} overlap.
+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
-- 
2.39.2


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 21:29     ` Alejandro Colomar
@ 2023-04-11 14:28       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 28+ messages in thread
From: Siddhesh Poyarekar @ 2023-04-11 14:28 UTC (permalink / raw)
  To: Alejandro Colomar, Florian Weimer, libc-alpha

On 2023-04-06 17:29, Alejandro Colomar wrote:
> Hi Siddhesh,
> 
> On 4/6/23 16:22, Siddhesh Poyarekar wrote:
> [...]
> 
>>> +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);
>>
>> size == 0 is undefined anyway; we return without touching the dest
>> because that's convenient for us.  OK.
> 
> Is it really convenient?  What real code benefits of that behavior?
> If we remove the conditional it's one less op.

Hmm, and then maybe abort in __strlcpy_chk for size == 0.

Thanks,
Sid

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-09 15:29     ` Paul Eggert
@ 2023-04-13 11:37       ` Florian Weimer
  2023-04-13 14:39         ` Paul Eggert
  0 siblings, 1 reply; 28+ messages in thread
From: Florian Weimer @ 2023-04-13 11:37 UTC (permalink / raw)
  To: Paul Eggert; +Cc: libc-alpha

* Paul Eggert:

> +The behavior is undefined if @var{to} or @var{from} is a null pointer,
> +or if the destination array's size is both less than @var{size} and
> +less than or equal to the length of the string @var{from}, or if
> +the string @var{from} overlaps the result (that is, if @var{from}
> +overlaps the first @samp{MIN (@var{size}, strlen (@var{from}) + 1)}
> +bytes of the the destination array @var{to}).

This makes it defined to call strlcpy with an incorrect (too small)
destination buffer size as long as the source string is short enough.
Is this really necessary?  The fortified implementation checks the
destination buffer size unconditionally (like we do for other
functions), and it does not match this documentation as a result.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-13 11:37       ` Florian Weimer
@ 2023-04-13 14:39         ` Paul Eggert
  2023-04-13 17:59           ` Paul Eggert
  0 siblings, 1 reply; 28+ messages in thread
From: Paul Eggert @ 2023-04-13 14:39 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

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

On 4/13/23 04:37, Florian Weimer wrote:
> This makes it defined to call strlcpy with an incorrect (too small)
> destination buffer size as long as the source string is short enough.
> Is this really necessary?  The fortified implementation checks the
> destination buffer size unconditionally (like we do for other
> functions), and it does not match this documentation as a result.

Oh, good point. Revised patch attached. It also says behavior is 
undefined if the source string overlaps the destination buffer. Although 
the fortified implementation doesn't check that, we should preserve the 
ability to check for that in the future, as it's a true danger sign.

[-- Attachment #2: 0001-manual-improve-strlcpy-strlcat-doc.patch --]
[-- Type: text/x-patch, Size: 8190 bytes --]

From 3e707d185c99c6dea3dfe9bd29f27fea2a2cb794 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 8 Apr 2023 13:54:37 -0700
Subject: [PATCH] manual: improve strlcpy/strlcat doc

* NEWS: Remove duplicate announcement of strlcpy/strlcat.
* manual/maint.texi (Source Fortification):
Mention strlcpy and strlcat.
* manual/string.texi (Truncating Strings): In strlcpy/strlcat,
mention that the caller should ensure room for the terminating
null byte, as draft POSIX does.  Don't say that the behavior
is undefined merely because the size is zero, since it's well defined.
Accurately describe the constraints imposed by 'restrict',
which are stricter than the constraints imposed by not allowing
overlapping copies.
Use the same language about string-processing choice
and about performance that we already use for strncpy and strncat.
Avoid unnecessarily-different wording between strlcpy and strlcat
that might cause the reader to mistakenly think of other differences.
Mention that strlcpy is O(max(size, strlen(src))), not O(size).
---
 NEWS               |  4 +--
 manual/maint.texi  |  4 +++
 manual/string.texi | 61 ++++++++++++++++++++++++++++++----------------
 3 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/NEWS b/NEWS
index 60b40fabcf..b21c4c10aa 100644
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,7 @@ 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 versions.
+  from OpenBSD, and are expected to be added to a future POSIX version.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
@@ -226,8 +226,6 @@ Major new features:
 
   The LoongArch ABI is 64-bit little-endian.
 
-* The functions strlcpy and strlcat have been added.
-
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Support for prelink will be removed in the next release; this includes
diff --git a/manual/maint.texi b/manual/maint.texi
index a8441e20b6..3ad4647cf3 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}
diff --git a/manual/string.texi b/manual/string.texi
index 57e3f6a619..a14ee08c0c 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}.
 
@@ -1079,13 +1080,15 @@ issues.  @xref{Concatenating Strings}.
 @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 is similar to @code{strcpy}, but copies at most
-@var{size} bytes from the string @var{from} into the destination
-array @var{to}, including a terminating null byte.
+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 all of the string @var{from} to the destination
-array @var{to}, including the terminating null byte.  Like other
+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.
 
@@ -1094,13 +1097,20 @@ If @var{size} is nonzero and less than or equal to the the length of the string
 bytes to the destination array @var{to}, and writes a terminating null
 byte to the last byte of the array.
 
-The return value @var{result} of @code{strlcpy} is the length of the
-string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
-true whenever truncation occurs.
+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 of @code{strlcpy} is undefined if @var{size} is zero, or if
-the source string and the first @var{size} bytes of the destination
-array overlap.
+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 result (that is, if the string
+@var{from} overlaps the first @samp{MIN (@var{size}, strlen
+(@var{from}) + 1)} bytes of the the destination array @var{to}).
+
+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
@@ -1109,8 +1119,9 @@ This function is derived from OpenBSD 2.4.
 @standards{BSD, string.h}
 @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 This function appends the string @var{from} to the
-string @var{to}, limiting the total size of the result string at
-@var{to} (including the null terminator) to @var{size}.
+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
@@ -1120,12 +1131,20 @@ 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 unless
-the returned value is less than @var{size}.
+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 total result (that is,
+if @var{from} overlaps the first @samp{MIN (@var{size}, strlen
+(@var{from}) + strlen (@var{to}) + 1)} bytes of the destination array
+@var{to}).
 
-The behavior is undefined if the array at @var{to} does not contain a
-null byte in its first @var{size} bytes, or if the source string and the
-first @var{size} bytes of @var{to} overlap.
+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
-- 
2.39.2


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-13 14:39         ` Paul Eggert
@ 2023-04-13 17:59           ` Paul Eggert
  0 siblings, 0 replies; 28+ messages in thread
From: Paul Eggert @ 2023-04-13 17:59 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

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

On 4/13/23 07:39, Paul Eggert wrote:
> Oh, good point. Revised patch attached. It also says behavior is 
> undefined if the source string overlaps the destination buffer.

Ouch, I sent the wrong patch. Sorry about that. Please look at the 
attached instead; it matches the intent stated above (plus it's shorter 
and easier to read).

[-- Attachment #2: 0001-manual-improve-strlcpy-strlcat-doc.patch --]
[-- Type: text/x-patch, Size: 8053 bytes --]

From 5062ee138dafff55c460d0349b2c7fb46713a9a1 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 8 Apr 2023 13:54:37 -0700
Subject: [PATCH] manual: improve strlcpy/strlcat doc

* NEWS: Remove duplicate announcement of strlcpy/strlcat.
* manual/maint.texi (Source Fortification):
Mention strlcpy and strlcat.
* manual/string.texi (Truncating Strings): In strlcpy/strlcat,
mention that the caller should ensure room for the terminating
null byte, as draft POSIX does.  Don't say that the behavior
is undefined merely because the size is zero, since it's well defined.
Accurately describe the constraints imposed by 'restrict',
which are stricter than the constraints imposed by not allowing
overlapping copies.  Also say that behavior is undefined if
the source string and destination array overlap.
Use the same language about string-processing choice
and about performance that we already use for strncpy and strncat.
Avoid unnecessarily-different wording between strlcpy and strlcat
that might cause the reader to mistakenly think of other differences.
Mention that strlcpy is O(max(size, strlen(src))), not O(size).
---
 NEWS               |  4 +---
 manual/maint.texi  |  4 ++++
 manual/string.texi | 58 +++++++++++++++++++++++++++++-----------------
 3 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/NEWS b/NEWS
index 60b40fabcf..b21c4c10aa 100644
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,7 @@ 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 versions.
+  from OpenBSD, and are expected to be added to a future POSIX version.
 
 Deprecated and removed features, and other changes affecting compatibility:
 
@@ -226,8 +226,6 @@ Major new features:
 
   The LoongArch ABI is 64-bit little-endian.
 
-* The functions strlcpy and strlcat have been added.
-
 Deprecated and removed features, and other changes affecting compatibility:
 
 * Support for prelink will be removed in the next release; this includes
diff --git a/manual/maint.texi b/manual/maint.texi
index a8441e20b6..3ad4647cf3 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}
diff --git a/manual/string.texi b/manual/string.texi
index 57e3f6a619..06fe5490ec 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}.
 
@@ -1079,13 +1080,15 @@ issues.  @xref{Concatenating Strings}.
 @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 is similar to @code{strcpy}, but copies at most
-@var{size} bytes from the string @var{from} into the destination
-array @var{to}, including a terminating null byte.
+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 all of the string @var{from} to the destination
-array @var{to}, including the terminating null byte.  Like other
+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.
 
@@ -1094,13 +1097,19 @@ If @var{size} is nonzero and less than or equal to the the length of the string
 bytes to the destination array @var{to}, and writes a terminating null
 byte to the last byte of the array.
 
-The return value @var{result} of @code{strlcpy} is the length of the
-string @var{from}.  This means that @samp{@var{result} >= @var{size}} is
-true whenever truncation occurs.
+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 of @code{strlcpy} is undefined if @var{size} is zero, or if
-the source string and the first @var{size} bytes of the destination
-array overlap.
+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
@@ -1109,8 +1118,9 @@ This function is derived from OpenBSD 2.4.
 @standards{BSD, string.h}
 @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 This function appends the string @var{from} to the
-string @var{to}, limiting the total size of the result string at
-@var{to} (including the null terminator) to @var{size}.
+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
@@ -1120,12 +1130,18 @@ 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 unless
-the returned value is less than @var{size}.
+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.
 
-The behavior is undefined if the array at @var{to} does not contain a
-null byte in its first @var{size} bytes, or if the source string and the
-first @var{size} bytes of @var{to} overlap.
+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
-- 
2.39.2


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-08 22:08   ` Paul Eggert
  2023-04-09 15:29     ` Paul Eggert
@ 2023-04-20  8:07     ` Florian Weimer
  2023-04-21 19:00       ` Paul Eggert
  1 sibling, 1 reply; 28+ messages in thread
From: Florian Weimer @ 2023-04-20  8:07 UTC (permalink / raw)
  To: Paul Eggert; +Cc: libc-alpha

* Paul Eggert:

>> +extern __typeof (strlcpy) __strlcpy;
>> +libc_hidden_proto (__strlcpy)
>> +extern __typeof (strlcat) __strlcat;
>> +libc_hidden_proto (__strlcat)
>
> Glibc shouldn't call these functions internally, so let's not export
> them to elsewhere in glibc.

strlcpy looks like it could be called for implementing %s in snprintf.
That seems like a reasonable optimization.  We would even use the
returned length in case the string does not fit.

Less sure about strlcat, we could drop the PLT avoidance for that, I
assume.

>> +	  /* 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';
>
> This micro-optimization is incorrect, as it's valid for dest to equal

I think we concluded that this optimization is in fact correct, right?

> src + size - 1, and that means the memcpy overlaps which is
> undefined. Change it to memcpy (dest, src, size - 1) and lose the
> comment. Or change it to memmove and lengthen the comment. Or better
> yet, get rid of all code like this (there are other instances), and
> use the simple OpenBSD implementation which will more likely match
> what callers expect (in the rare cases where the behaviors differ) and
> will possibly be faster despite not using memcpy.

I expect someone to rewrite this using word-size accesses fairly soon.
I think using strlen and memcpy more clearly documents the intent than
the explicit loops.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-06 14:22   ` Siddhesh Poyarekar
  2023-04-06 15:09     ` Florian Weimer
  2023-04-06 21:29     ` Alejandro Colomar
@ 2023-04-20 10:55     ` Florian Weimer
  2023-04-20 11:45       ` Siddhesh Poyarekar
  2 siblings, 1 reply; 28+ messages in thread
From: Florian Weimer @ 2023-04-20 10:55 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

* Siddhesh Poyarekar:

>> 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)
>
> Do we want to delay doing this until we have an actual internal use of
> these interfaces?

The *_chk functions need these aliases today.

>> +__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 */
>> +
>
> Couldn't we use the __glibc_fortify macros here?

Do you have a concrete proposal?  I was just following what we have for
stpncpy and other functions.

I don't think it's possible to use the generic macros for
wcslcpy/wcslcat because of the bytes vs wide characters distinction.

>> +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);
>
> size == 0 is undefined anyway; we return without touching the dest
> because that's convenient for us.  OK.

size == 0 is defined in OpenBSD via snprintf equivalence, but maybe
that's over-interpreting the manual page.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-20 10:55     ` Florian Weimer
@ 2023-04-20 11:45       ` Siddhesh Poyarekar
  2023-04-21 17:45         ` Florian Weimer
  0 siblings, 1 reply; 28+ messages in thread
From: Siddhesh Poyarekar @ 2023-04-20 11:45 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On 2023-04-20 06:55, Florian Weimer wrote:
> * Siddhesh Poyarekar:
> 
>>> 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)
>>
>> Do we want to delay doing this until we have an actual internal use of
>> these interfaces?
> 
> The *_chk functions need these aliases today.

Ack.

>>> +__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 */
>>> +
>>
>> Couldn't we use the __glibc_fortify macros here?
> 
> Do you have a concrete proposal?  I was just following what we have for
> stpncpy and other functions.

I don't, but I'll give it a shot.

> I don't think it's possible to use the generic macros for
> wcslcpy/wcslcat because of the bytes vs wide characters distinction.

Hmm, there's __glibc_fortify_n for wide chars which __wcsncpy_chk uses, 
or have I misunderstood your comment?

>>> +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);
>>
>> size == 0 is undefined anyway; we return without touching the dest
>> because that's convenient for us.  OK.
> 
> size == 0 is defined in OpenBSD via snprintf equivalence, but maybe
> that's over-interpreting the manual page.

Doesn't seem like over-interpreting to me:

"The strlcpy() and strlcat() functions copy and concatenate strings with 
the same input parameters and output result as snprintf(3)"

We should probably just pick a lane then (and specify it in the manual), 
and snprintf equivalence is probably a reasonable take in terms of safety.

Thanks,
Sid

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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-20 11:45       ` Siddhesh Poyarekar
@ 2023-04-21 17:45         ` Florian Weimer
  0 siblings, 0 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-21 17:45 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: libc-alpha

* Siddhesh Poyarekar:

>> I don't think it's possible to use the generic macros for
>> wcslcpy/wcslcat because of the bytes vs wide characters distinction.
>
> Hmm, there's __glibc_fortify_n for wide chars which __wcsncpy_chk
> uses, or have I misunderstood your comment?

The new w*_chk APIs have both byte-size and character-size arguments,
based on an expectation that it simplifies passing dynamic object sizes
(because allocators measure sizes in bytes).

>> size == 0 is defined in OpenBSD via snprintf equivalence, but maybe
>> that's over-interpreting the manual page.
>
> Doesn't seem like over-interpreting to me:
>
> "The strlcpy() and strlcat() functions copy and concatenate strings
> with the same input parameters and output result as snprintf(3)"

Well, that definitely requires *some* interpretation because you cannot
implement strlcat directly with snprintf because snprintf inputs and the
output buffer cannot overlap. 8-)  So it's difficult for me to see how
the size == 0 case would be covered.

Thanks,
Florian


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-20  8:07     ` Florian Weimer
@ 2023-04-21 19:00       ` Paul Eggert
  2023-04-28  8:49         ` Florian Weimer
  0 siblings, 1 reply; 28+ messages in thread
From: Paul Eggert @ 2023-04-21 19:00 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On 2023-04-20 01:07, Florian Weimer wrote:
> * Paul Eggert:
> 
>>> +extern __typeof (strlcpy) __strlcpy;
>>> +libc_hidden_proto (__strlcpy)
>>> +extern __typeof (strlcat) __strlcat;
>>> +libc_hidden_proto (__strlcat)
>>
>> Glibc shouldn't call these functions internally, so let's not export
>> them to elsewhere in glibc.
> 
> strlcpy looks like it could be called for implementing %s in snprintf.
> That seems like a reasonable optimization.

No, because strlcpy must return the length of the source even when it's 
longer than INT_MAX. (This is a botch in the spec which we apparently 
cannot fix.) So there's no way snprintf could use strlcpy without 
hurting worst-case performance.


> Less sure about strlcat, we could drop the PLT avoidance for that, I
> assume.

Let's drop it for both. If there's ever a real need for either (which I 
doubt) we can add it as needed.


> I expect someone to rewrite this using word-size accesses fairly soon.

That would be headed in the wrong direction. We should not waste time 
trying to optimize these functions' copying actions, as the destinations 
are invariably so small that our attempts to "optimize" will likely hurt 
performance.


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

* Re: [PATCH 1/2] Implement strlcpy and strlcat [BZ #178]
  2023-04-21 19:00       ` Paul Eggert
@ 2023-04-28  8:49         ` Florian Weimer
  0 siblings, 0 replies; 28+ messages in thread
From: Florian Weimer @ 2023-04-28  8:49 UTC (permalink / raw)
  To: Paul Eggert; +Cc: libc-alpha

* Paul Eggert:

> On 2023-04-20 01:07, Florian Weimer wrote:
>> * Paul Eggert:
>> 
>>>> +extern __typeof (strlcpy) __strlcpy;
>>>> +libc_hidden_proto (__strlcpy)
>>>> +extern __typeof (strlcat) __strlcat;
>>>> +libc_hidden_proto (__strlcat)
>>>
>>> Glibc shouldn't call these functions internally, so let's not export
>>> them to elsewhere in glibc.
>> strlcpy looks like it could be called for implementing %s in
>> snprintf.
>> That seems like a reasonable optimization.
>
> No, because strlcpy must return the length of the source even when
> it's longer than INT_MAX. (This is a botch in the spec which we
> apparently cannot fix.) So there's no way snprintf could use strlcpy
> without hurting worst-case performance.

The possible shortcut only exists in the EOVERFLOW case, otherwise we
need the exact length for the snprintf return value.  I think optimizing
EOVERFLOW is not important.  We will get more benefit from sharing the
strlcpy implementation.

>> I expect someone to rewrite this using word-size accesses fairly soon.
>
> That would be headed in the wrong direction. We should not waste time
> trying to optimize these functions' copying actions, as the
> destinations are invariably so small that our attempts to "optimize"
> will likely hurt performance.

I expect that these optimizations will benefit short strings as well,
similar to the other string functions.

Thanks,
Florian


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

end of thread, other threads:[~2023-04-28  8:49 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-05 11:20 [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Florian Weimer
2023-04-05 11:20 ` [PATCH 1/2] Implement strlcpy and strlcat [BZ #178] Florian Weimer
2023-04-05 13:18   ` Adhemerval Zanella Netto
2023-04-06  9:18     ` Florian Weimer
2023-04-06 14:22   ` Siddhesh Poyarekar
2023-04-06 15:09     ` Florian Weimer
2023-04-06 21:29     ` Alejandro Colomar
2023-04-11 14:28       ` Siddhesh Poyarekar
2023-04-20 10:55     ` Florian Weimer
2023-04-20 11:45       ` Siddhesh Poyarekar
2023-04-21 17:45         ` Florian Weimer
2023-04-06 21:21   ` Alejandro Colomar
2023-04-06 21:35     ` Florian Weimer
2023-04-06 22:15       ` Alejandro Colomar
2023-04-06 22:19       ` Alejandro Colomar
2023-04-06 22:34     ` Alejandro Colomar
2023-04-08 22:08   ` Paul Eggert
2023-04-09 15:29     ` Paul Eggert
2023-04-13 11:37       ` Florian Weimer
2023-04-13 14:39         ` Paul Eggert
2023-04-13 17:59           ` Paul Eggert
2023-04-20  8:07     ` Florian Weimer
2023-04-21 19:00       ` Paul Eggert
2023-04-28  8:49         ` Florian Weimer
2023-04-05 11:20 ` [PATCH 2/2] Add the wcslcpy, wcslcat functions Florian Weimer
2023-04-08 22:09   ` Paul Eggert
2023-04-05 12:30 ` [PATCH 0/2] strlcpy/strlcat/wcslcpy/wcscat implementation Alejandro Colomar
2023-04-08 22:05 ` Paul Eggert

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