Add strlcpy, strlcat [BZ #178]

Message ID 4c1cad2f-97b0-8eb2-199e-1f15f085eeb2@redhat.com
State Superseded
Headers

Commit Message

Florian Weimer June 16, 2017, 10:25 a.m. UTC
  I want to start again the discussion to include strlcpy and strlcat in
glibc.  The attached patch has been rebased to current master (changing
the symbol versions to GLIBC_2.26).

I don't think there are any technical issues with the patch left, so the
remaining question is whether we can reach consensus.

Thanks,
Florian
  

Comments

Paul Eggert June 16, 2017, 5:09 p.m. UTC | #1
On 06/16/2017 03:25 AM, Florian Weimer wrote:
> I want to start again the discussion to include strlcpy and strlcat in
> glibc.

Technical progress in other areas has rendered this old proposal even 
less attractive than it was before. My laptop now supports gcc 
-fcheck-pointer-bounds, and Intel CET promises to resist buffer overrun 
attacks more conveniently and systematically. Why not see how these 
approaches play out, and in the meantime let strlcpy's sleeping dogs lie?
  
Florian Weimer June 16, 2017, 9:08 p.m. UTC | #2
On 06/16/2017 07:09 PM, Paul Eggert wrote:
> On 06/16/2017 03:25 AM, Florian Weimer wrote:
>> I want to start again the discussion to include strlcpy and strlcat in
>> glibc.
> 
> Technical progress in other areas has rendered this old proposal even
> less attractive than it was before. My laptop now supports gcc
> -fcheck-pointer-bounds, and Intel CET promises to resist buffer overrun
> attacks more conveniently and systematically. Why not see how these
> approaches play out, and in the meantime let strlcpy's sleeping dogs lie?

Intel MPX in the GNU toolchain is for experiments only.  Performance is
worse than Address Sanitizer according to this report:

  https://intel-mpx.github.io/performance/

The implementation of memcpy/memmove indeed looks horribly expensive.

By default, the MPX run-time does not terminate the process after
detecting a bounds-checking violation, so it is more of a debugging aid.

MPX uses the same broken approach as Address Sanitizer to use bounds
information along with libc functions: hand-written wrappers.  This
means that each time we add a function to glibc (such as explicit_bzero
or reallocarray, or even the C11 function aligned_alloc), we break MPX
support.  The range of wrappers offered by the MPX run-time in GCC is
also extremely limited and does not cover important areas such as I/O
functions.

Except for the malloc wrappers in GCC, I could not find *any* memory
allocators with MPX support.  The effort to implement them for Python
has stalled:

  https://bugs.python.org/issue25300

jemalloc, for example, would need MPX support for its alternative
allocator entry points.  APR would need MPX support for its pool
allocator, obstacks would need support, and so on.  (TLS works
correctly, though, because GCC uses the type information implied by it.)

Even GCC's own operator new isn't MPX-aware.

And of course, a whole distribution would have to be compiled with MPX
support.  I'm not aware of any distribution doing that, or anyone
working on making it happen.

In short, it is fair to say that MPX isn't available today, even though
current silicon may support this.

Does this alter your opinion regarding strlcpy/strlcat?

Thanks,
Florian
  
Rical Jasan June 17, 2017, 4:52 p.m. UTC | #3
On 06/16/2017 03:25 AM, Florian Weimer wrote:
> diff --git a/debug/Versions b/debug/Versions
> index a6628db..3dd0f7f 100644
> --- a/debug/Versions
> +++ b/debug/Versions
> @@ -57,6 +57,9 @@ libc {
>    }
>    GLIBC_2.25 {
>      __explicit_bzero_chk;
> +  GLIBC_2.26 {
> +    __strlcpy_chk;
> +    __strlcat_chk;
>    }

Missing brace?

> diff --git a/manual/string.texi b/manual/string.texi
> index ac02c6d..963c587 100644
> --- a/manual/string.texi
> +++ b/manual/string.texi
> @@ -1071,6 +1071,79 @@ processing text.  Also, this function has significant performance
>  issues.  @xref{Concatenating Strings}.
>  @end deftypefun
>  
> +@comment string.h
> +@comment BSD
> +@deftypefun size_t strlcpy (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})

Will need to be "@standards{BSD, string.h}" now.

> +@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.
> +
> +As noted below, this function is generally a poor choice for processing
> +text.  Unlike @code{strncpy}, @code{strlcpy} requires @var{size} to be
> +nonzero and the source string to be null-terminated, computes the
> +source string's length, ensures that the destination is
> +null-terminated, and does not fill the remaining part of the destination
> +with null bytes.
> +
> +This function is derived from OpenBSD.

Do you have a version for this?

> +@end deftypefun
> +
> +@comment string.h
> +@comment BSD
> +@deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})

@standards{BSD, string.h}, following the @def.

> +@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} are copied).

"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.
> +
> +As noted below, this function is generally a poor choice for processing
> +text.  Also, this function has significant performance issues.
> +@xref{Concatenating Strings}.  Unlike @code{strncat}, @var{size}
> +specifies the maximum total size of the result string (including its
> +null terminator), not the number of bytes copied from the source string
> +@var{from}.
> +Also, unlike @code{strncat} this function requires the source and
> +destination to be null-terminated, computes the source string's
> +length, and keeps the destination null-terminated.

Using, "; @pxref{Concatenating Strings}" would help associate that
reference to the "significant performance issues" more strongly.

To alleviate the repetition of "also" and "unlike", I would move the
comma after the second "Also" after "@code{strncat}".

The line break is also odd for new content.  Collaborative artefact maybe?

> +
> +This function is derived from OpenBSD.

The version again, if you have it, would be nice.


Rical
  

Patch

Implement strlcpy and strlcat [BZ #178]

This change may cause application code not to compile anymore, but
strlcpy is in the implementation namespace (per C11 7.31.13), so this
is an application issue.

2017-06-16  Paul Eggert  <eggert@cs.ucla.edu>

	Document strlcpy, strlcat
	[BZ #178]
	This patch was partly derived from text by Florian Weimer in:
	https://sourceware.org/ml/libc-alpha/2015-12/msg00593.html
	* manual/string.texi (Truncating Strings): New functions from
	OpenBSD.

2017-06-16  Florian Weimer  <fweimer@redhat.com>

	[BZ #178]
	* string/Makefile (routines): Add strlcpy, strlcat.
	(tests): Add tst-strlcpy, tst-strlcat.
	* string/Versions (2.26): Export strlcpy, strlcat.
	* string/string.h (strlcpy, strlcat): Add.
	* string/bits/string3.h (__warn_strlcpy_size_zero)
	(__warn_strlcpy_size_large, __strlcpy_chk, __strlcpy_alias, strlcpy)
	(__warn_strlcat_size_zero, __warn_strlcat_size_large)
	(__strlcat_chk, __strlcat_alias, strlcat): Add.
	* string/strlcpy.c: New file.
	* string/strlcat.c: New file.
	* string/tst-strlcpy.c: Likewise.
	* string/tst-strlcat.c: Likewise.
	* include/string.h (strlcpy, strlcat): Declare as hidden.
	* debug/Makefile (routines): Add strlcpy_chk, strlcat_chk.
	* debug/Versions (2.26): Export __strlcpy_chk, __strlcat_chk.
	* debug/strlcpy_chk.c: New file.
	* debug/strlcat_chk.c: New file.
	* debug/tst-chk1.c (doit): Test strlcpy, strlcat.
	* sysdeps/*/libc.abilist: Add strlcpy, __strlcpy_chk, strlcat,
	__strlcat_chk.

diff --git a/NEWS b/NEWS
index 804c1b9..f871f83 100644
--- a/NEWS
+++ b/NEWS
@@ -98,6 +98,9 @@  Version 2.26
 * The tunables feature is now enabled by default.  This allows users to tweak
   behavior of the GNU C Library using the GLIBC_TUNABLES environment variable.
 
+* The GNU C Library now includes implementations of strlcpy and strlcat.
+  Contributed by Florian Weimer (Red Hat).
+
 Security related changes:
 
 * The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes,
diff --git a/debug/Makefile b/debug/Makefile
index cd4975c..b36e23b 100644
--- a/debug/Makefile
+++ b/debug/Makefile
@@ -30,6 +30,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 a6628db..3dd0f7f 100644
--- a/debug/Versions
+++ b/debug/Versions
@@ -57,6 +57,9 @@  libc {
   }
   GLIBC_2.25 {
     __explicit_bzero_chk;
+  GLIBC_2.26 {
+    __strlcpy_chk;
+    __strlcat_chk;
   }
   GLIBC_PRIVATE {
     __fortify_fail;
diff --git a/debug/strlcat_chk.c b/debug/strlcat_chk.c
new file mode 100644
index 0000000..c7b9f03
--- /dev/null
+++ b/debug/strlcat_chk.c
@@ -0,0 +1,32 @@ 
+/* Fortified version of strlcat.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <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 0000000..842f44f
--- /dev/null
+++ b/debug/strlcpy_chk.c
@@ -0,0 +1,32 @@ 
+/* Fortified version of strlcpy.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <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-chk1.c b/debug/tst-chk1.c
index 60c8e1e..4a87a2e 100644
--- a/debug/tst-chk1.c
+++ b/debug/tst-chk1.c
@@ -509,6 +509,16 @@  do_test (void)
   strncpy (a.buf1 + (O + 6), "X", l0 + 4);
   CHK_FAIL_END
 
+  CHK_FAIL_START
+  strlcpy (buf, "", sizeof (buf) + 1);
+  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);
@@ -532,6 +542,18 @@  do_test (void)
   CHK_FAIL_START
   strncat (a.buf1, "ZYXWV", l0 + 3);
   CHK_FAIL_END
+
+  buf[0] = '\0';
+  CHK_FAIL_START
+  strlcat (buf, "ZYXWV", sizeof (buf) + 1);
+  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 2bf2944..ad7c217 100644
--- a/include/string.h
+++ b/include/string.h
@@ -78,6 +78,8 @@  extern __typeof (strncasecmp_l) __strncasecmp_l;
 libc_hidden_proto (__mempcpy)
 libc_hidden_proto (__stpcpy)
 libc_hidden_proto (__stpncpy)
+libc_hidden_proto (strlcpy)
+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 ac02c6d..963c587 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -1071,6 +1071,79 @@  processing text.  Also, this function has significant performance
 issues.  @xref{Concatenating Strings}.
 @end deftypefun
 
+@comment string.h
+@comment BSD
+@deftypefun size_t strlcpy (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
+@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.
+
+As noted below, this function is generally a poor choice for processing
+text.  Unlike @code{strncpy}, @code{strlcpy} requires @var{size} to be
+nonzero and the source string to be null-terminated, computes the
+source string's length, ensures that the destination is
+null-terminated, and does not fill the remaining part of the destination
+with null bytes.
+
+This function is derived from OpenBSD.
+@end deftypefun
+
+@comment string.h
+@comment BSD
+@deftypefun size_t strlcat (char *restrict @var{to}, const char *restrict @var{from}, size_t @var{size})
+@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} are 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.
+
+As noted below, this function is generally a poor choice for processing
+text.  Also, this function has significant performance issues.
+@xref{Concatenating Strings}.  Unlike @code{strncat}, @var{size}
+specifies the maximum total size of the result string (including its
+null terminator), not the number of bytes copied from the source string
+@var{from}.
+Also, unlike @code{strncat} this function requires the source and
+destination to be null-terminated, computes the source string's
+length, and keeps the destination null-terminated.
+
+This function is derived from OpenBSD.
+@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 8d0d6b0..526ee4b 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -42,7 +42,8 @@  routines	:= strcat strchr strcmp strcoll strcpy strcspn		\
 				     addsep replace)			\
 		   envz basename					\
 		   strcoll_l strxfrm_l string-inlines memrchr		\
-		   xpg-strerror strerror_l explicit_bzero
+		   xpg-strerror strerror_l explicit_bzero 		\
+		   strlcpy strlcat
 
 strop-tests	:= memchr memcmp memcpy memmove mempcpy memset memccpy	\
 		   stpcpy stpncpy strcat strchr strcmp strcpy strcspn	\
@@ -57,7 +58,7 @@  tests		:= tester inl-tester noinl-tester testcopy test-ffs	\
 		   bug-strtok1 $(addprefix test-,$(strop-tests))	\
 		   bug-envz1 tst-strxfrm2 tst-endian tst-svc2		\
 		   tst-strtok_r bug-strcoll2 tst-cmp tst-xbzero-opt	\
-		   test-endian-types
+		   test-endian-types tst-strlcpy tst-strlcat
 
 # This test allocates a lot of memory and can run for a long time.
 xtests = tst-strcoll-overflow
diff --git a/string/Versions b/string/Versions
index 9b709d1..65368c2 100644
--- a/string/Versions
+++ b/string/Versions
@@ -85,4 +85,8 @@  libc {
   GLIBC_2.25 {
     explicit_bzero;
   }
+  GLIBC_2.26 {
+    strlcpy;
+    strlcat;
+  }
 }
diff --git a/string/bits/string3.h b/string/bits/string3.h
index 738226d..8e85a0d 100644
--- a/string/bits/string3.h
+++ b/string/bits/string3.h
@@ -40,6 +40,8 @@  __warndecl (__warn_memset_zero_len,
 #  undef stpcpy
 # endif
 # ifdef __USE_MISC
+#  undef strlcpy
+#  undef strlcat
 #  undef bcopy
 #  undef bzero
 # endif
@@ -154,3 +156,59 @@  __NTH (strncat (char *__restrict __dest, const char *__restrict __src,
 {
   return __builtin___strncat_chk (__dest, __src, __len, __bos (__dest));
 }
+
+#ifdef __USE_MISC
+__warndecl (__warn_strlcpy_size_zero,
+	    "strlcpy used with a size argument of zero");
+__warndecl (__warn_strlcpy_size_large,
+	    "strlcpy used with a size argument which is too large");
+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 __len))
+{
+  if (__builtin_constant_p (__len == 0) && __len == 0)
+    {
+      __warn_strlcpy_size_zero ();
+      return 0;
+    }
+  if (__builtin_constant_p (__len > __bos (__dest)) && __len > __bos (__dest))
+    __warn_strlcpy_size_large ();
+  if (__builtin_constant_p (__bos (__dest) == (size_t) -1)
+      && __bos (__dest) == (size_t) -1)
+    return __strlcpy_alias (__dest, __src, __len);
+  return __strlcpy_chk (__dest, __src, __len, __bos (__dest));
+}
+
+__warndecl (__warn_strlcat_size_zero,
+	    "strlcat used with a size argument of zero");
+__warndecl (__warn_strlcat_size_large,
+	    "strlcat used with a size argument which is too large");
+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 __len))
+{
+  if (__builtin_constant_p (__len == 0) && __len == 0)
+    {
+      __warn_strlcat_size_zero ();
+      return strlen (__src);
+    }
+  if (__builtin_constant_p (__len > __bos (__dest)) && __len > __bos (__dest))
+    __warn_strlcat_size_large ();
+  if (__builtin_constant_p (__bos (__dest) == (size_t) -1)
+      && __bos (__dest) == (size_t) -1)
+    return __strlcat_alias (__dest, __src, __len);
+  return __strlcat_chk (__dest, __src, __len, __bos (__dest));
+}
+#endif
diff --git a/string/string.h b/string/string.h
index d1a2746..8af9f1e 100644
--- a/string/string.h
+++ b/string/string.h
@@ -460,6 +460,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 ((2));
+
+/* 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 ((2));
+#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 0000000..fbee540
--- /dev/null
+++ b/string/strlcat.c
@@ -0,0 +1,60 @@ 
+/* Append a null-terminated string to another string, with length checking.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <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)
diff --git a/string/strlcpy.c b/string/strlcpy.c
new file mode 100644
index 0000000..c04c1d4
--- /dev/null
+++ b/string/strlcpy.c
@@ -0,0 +1,47 @@ 
+/* Copy a null-terminated string to a fixed-size buffer, with length checking.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <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)
diff --git a/string/tst-strlcat.c b/string/tst-strlcat.c
new file mode 100644
index 0000000..2e841af
--- /dev/null
+++ b/string/tst-strlcat.c
@@ -0,0 +1,93 @@ 
+/* Test the strlcat function.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CHECK(cond)					\
+  if (!(cond))						\
+    {							\
+      printf ("%s:%d: FAIL\n", __FILE__, __LINE__);	\
+      exit (1);						\
+    }
+
+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));
+  CHECK (strlcat (s.buf1, "", 0) == 0);
+  CHECK (memcmp (&s, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+  CHECK (strlcat (s.buf1, "Hello!", 0) == 6);
+  CHECK (memcmp (&s, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+  CHECK (strlcat (NULL, "Hello!", 0) == 6);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "He");
+  CHECK (strlcat (s.buf1, "llo!", sizeof (s.buf1)) == 6);
+  CHECK (memcmp (&s, "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "H");
+  CHECK (strlcat (s.buf1, "ello, world!!!", sizeof (s.buf1)) == 15);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  /* 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");
+  CHECK (strlcat (s.buf1, ", world!!!!", sizeof (s.buf1)) == 16);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  /* An even longer source string is truncated as well, and the total
+     length is returned.  */
+  memset (&s, '@', sizeof (s));
+  strcpy (s.buf1, "Hello,");
+  CHECK (strlcat (s.buf1, " world!!!!!!!!", sizeof (s.buf1)) == 20);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  /* 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));
+  CHECK (strlcat (s.buf1, "", sizeof (s.buf1)) == 16);
+  CHECK (memcmp (&s, "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+  CHECK (strlcat (s.buf1, "Hello!", sizeof (s.buf1)) == 22);
+  CHECK (memcmp (&s, "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+  CHECK (strlcat (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)) == 36);
+  CHECK (memcmp (&s, "$$$$$$$$$$$$$$$$@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
diff --git a/string/tst-strlcpy.c b/string/tst-strlcpy.c
new file mode 100644
index 0000000..8635288
--- /dev/null
+++ b/string/tst-strlcpy.c
@@ -0,0 +1,77 @@ 
+/* Test the strlcpy function.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define CHECK(cond)					\
+  if (!(cond))						\
+    {							\
+      printf ("%s:%d: FAIL\n", __FILE__, __LINE__);	\
+      exit (1);						\
+    }
+
+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));
+  CHECK (strlcpy (s.buf1, "Hello!", 0) == 6);
+  CHECK (memcmp (&s, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+  CHECK (strlcpy (NULL, "Hello!", 0) == 6);
+
+  /* No bytes are are modified in the target buffer if the source
+     string is short enough.  */
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello!", sizeof (s.buf1)) == 6);
+  CHECK (memcmp (&s, "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+
+  /* A source string which fits exactly into the destination buffer is
+     not truncated.  */
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello, world!!!", sizeof (s.buf1)) == 15);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  /* 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));
+  CHECK (strlcpy (s.buf1, "Hello, world!!!!", sizeof (s.buf1)) == 16);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  /* An even longer source string is truncated as well, and the
+     original length is returned.  */
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello, world!!!!!!!!", sizeof (s.buf1)) == 20);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 81e4fe9..368b57e 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2098,8 +2098,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fab7331..e7c3155 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2009,11 +2009,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index d2a206a..07f58ea 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -99,11 +99,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.4 GLIBC_2.4 A
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 24bb730..c78467b 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1863,11 +1863,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 2ff1998..f46f425 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2021,11 +2021,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 39ccf2b..f3de373 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1885,11 +1885,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index b594ebd..6160252 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -100,11 +100,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.4 GLIBC_2.4 A
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index a36739d..86348c0 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1977,11 +1977,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 16aa254..9dcc6c5 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2098,8 +2098,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 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 907ab33..13a26e5 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1952,11 +1952,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 36ee235..c712066 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1950,11 +1950,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 783aa73..2c572dd 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1948,11 +1948,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index e1275df..011cc96 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1943,11 +1943,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index be25228..3be1d17 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2139,8 +2139,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 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 e213895..c16d5a6 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1981,11 +1981,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index d25aefd..be7cd7a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1986,11 +1986,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 5eb056b..be70578 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -100,11 +100,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 _Exit F
 GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 63d33e8..d64515c 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1981,11 +1981,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index b1b2b29..b739897 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1882,11 +1882,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index f3a70a0..50b901d 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1867,11 +1867,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 8c4c2e5..ba02e87 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1973,11 +1973,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 1653164..416c198 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1911,11 +1911,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index 41647d4..7affded 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2105,8 +2105,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 1088923..b2250e9 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2105,8 +2105,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index 41647d4..7affded 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2105,8 +2105,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 513524d..13b4fc0 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1862,11 +1862,15 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F
 GLIBC_2.3 GLIBC_2.3 A
 GLIBC_2.3 __ctype_b_loc F
 GLIBC_2.3 __ctype_tolower_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0c557e9..6d46187 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2105,8 +2105,12 @@  GLIBC_2.25 strfromd F
 GLIBC_2.25 strfromf F
 GLIBC_2.25 strfroml F
 GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 __strlcat_chk F
+GLIBC_2.26 __strlcpy_chk F
 GLIBC_2.26 preadv2 F
 GLIBC_2.26 preadv64v2 F
 GLIBC_2.26 pwritev2 F
 GLIBC_2.26 pwritev64v2 F
 GLIBC_2.26 reallocarray F
+GLIBC_2.26 strlcat F
+GLIBC_2.26 strlcpy F