Message ID | f2b8dcd6c84557b79f1398c0ca3e55d0f3b10584.1680693362.git.fweimer@redhat.com |
---|---|
State | New |
Headers | show |
Series | strlcpy/strlcat/wcslcpy/wcscat implementation | expand |
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?
* 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
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
* 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
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
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; >> +}
* 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
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
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
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) >
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.
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.
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
* 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
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.
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).
* 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
* 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
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
* 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
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.
* 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
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