Implement strlcpy, strlcat [BZ #178]

Message ID 9f360d0a-56cd-19b7-a170-4f41037fcc2d@redhat.com
State New, archived
Headers

Commit Message

Florian Weimer May 18, 2016, 12:29 p.m. UTC
  I would like to restart the discussion about inclusion of strlcpy and 
strlcat.

The attached patch is basically what I had before, rebased to master. 
The symbol versions changed as a result.

Paul Sturm noticed an infinite recursion in the fortification wrappers. 
I fixed that an added a test case as well.

Thanks,
Florian
  

Comments

Joseph Myers May 18, 2016, 3:28 p.m. UTC | #1
On Wed, 18 May 2016, Florian Weimer wrote:

> The attached patch is basically what I had before, rebased to master. The
> symbol versions changed as a result.

The "(2.23)" in the ChangeLog entries need to change as well.
  
Florian Weimer May 18, 2016, 5:10 p.m. UTC | #2
On 05/18/2016 05:28 PM, Joseph Myers wrote:
> On Wed, 18 May 2016, Florian Weimer wrote:
>
>> The attached patch is basically what I had before, rebased to master. The
>> symbol versions changed as a result.
>
> The "(2.23)" in the ChangeLog entries need to change as well.

Ack, I made the change locally.

Thanks,
Florian
  
Paul Eggert May 20, 2016, 5:21 p.m. UTC | #3
Two thoughts.

First, the September 2014 thread motivated strlcpy/strlcat in part by 
saying that Fedora has over 60 packages which define strlcpy/strlcat[1], 
the implication being that moving strlcpy/strlcat into glibc would fix 
more bugs than it would cause and would be worth the effort's cost. 
Skeptics like me can reasonably counter that the effort would likely 
just be security theater, in that it would not be of any real help but 
would merely exhibit activity in the security area, and possibly would 
even cause more problems than it fixes.

Data could help assuage skeptics' concerns. Although it's impossible to 
count bugs that are not discovered yet, we *do* now have twenty months' 
worth of data since September 2014. So: how many user-visible bugs 
and/or vulnerabilities have been discovered or reported in these 60-odd 
Fedora packages, bugs that would have been prevented by moving 
strlcpy/strlcat into a better-maintained library? New bugs cropping up 
would argue for change. Quietude would suggest that we leave things alone.


Second, the September 2014 thread said that another way to fortify 
strlcpy/strlcat, assuming we still want to do it, would be to use 
libbsd's strlcpy/strlcat implementation and add fortification to it. 
However, it was argued that this would be a Fedora-only effort and that 
libbsd contains functions like fgetln that are awkward. But isn't the 
point of libbsd to provide BSD functions that are awkward or 
questionable from the glibc point of view? And I don't see how work in 
libbsd would be Fedora-only, as other distributions like Debian also use 
libbsd, and whatever methods Fedora uses to merge strlcpy/strlcat into 
string.h would be methods that other distributions could use as well.

The Fedora bug report that corresponds to strlcpy/strlcat 
fortification[2] says that this is "Priority low Severity low", and from 
the above it appears that this is mainly a packaging problem instead of 
being a necessary addition to the core libc API.


[1] https://sourceware.org/ml/libc-alpha/2014-09/msg00357.html
[2] https://bugzilla.redhat.com/show_bug.cgi?id=1032523
  
Florian Weimer May 30, 2016, 3:40 p.m. UTC | #4
On 05/20/2016 07:21 PM, Paul Eggert wrote:
> Two thoughts.
>
> First, the September 2014 thread motivated strlcpy/strlcat in part by
> saying that Fedora has over 60 packages which define strlcpy/strlcat[1],
> the implication being that moving strlcpy/strlcat into glibc would fix
> more bugs than it would cause and would be worth the effort's cost.
> Skeptics like me can reasonably counter that the effort would likely
> just be security theater, in that it would not be of any real help but
> would merely exhibit activity in the security area, and possibly would
> even cause more problems than it fixes.

I'm not sure if it is possible to easily quantify the security benefits 
that strlcpy/strlcat bring.  Any project we start in this area would 
prevent us from working directly on glibc improvements, due to the 
resource constraints we face.

Back in 2014, I at least wasn't aware how widely strlcpy had been 
adopted by libcs.  Since then, I have done some research, and all Linux 
libcs I know of except glibc provide it, but it's available well beyond 
that:

   https://sourceware.org/glibc/wiki/strlcpy

The major omissions are glibc and MSVC.  Most of the other libcs in the 
“no” camp are legacy implementations to various degrees.

The compatibility doubts raised in 2015/earlier this year do not appear 
as significant as we assumed.

> Data could help assuage skeptics' concerns. Although it's impossible to
> count bugs that are not discovered yet, we *do* now have twenty months'
> worth of data since September 2014. So: how many user-visible bugs
> and/or vulnerabilities have been discovered or reported in these 60-odd
> Fedora packages, bugs that would have been prevented by moving
> strlcpy/strlcat into a better-maintained library?

At present, Fedora deliberately does not use libbsd widely (we 
eliminated the dependency from the Samba packages, for instance).  As a 
result, the data you are asking for is scattered across the entire 
distribution.  I don't know a good way to obtain it.

> Second, the September 2014 thread said that another way to fortify
> strlcpy/strlcat, assuming we still want to do it, would be to use
> libbsd's strlcpy/strlcat implementation and add fortification to it.
> However, it was argued that this would be a Fedora-only effort and that
> libbsd contains functions like fgetln that are awkward. But isn't the
> point of libbsd to provide BSD functions that are awkward or
> questionable from the glibc point of view?

fgetln is not questionable at all by itself (except when reading lines 
from a read-only mapping or in-memory source, so that the non-const 
return value is incorrect).  Its awkwardness is the result of the 
implementation in libbsd, on top of stdio interface (and not libio 
internals).

Once we have a clear picture of the vtable/ABI situation of libio, we 
can implement fgetln in glibc quite easily (with the caveat that writing 
to the returned buffer may not be allowed, depending on the underlying 
stream, but the BSDs have the same problem).  But that's a separate 
discussion.

> And I don't see how work in
> libbsd would be Fedora-only, as other distributions like Debian also use
> libbsd, and whatever methods Fedora uses to merge strlcpy/strlcat into
> string.h would be methods that other distributions could use as well.

I might be misremembering things, but I think back in 2014, libbsd 
maintainership in Debian languished a bit, and the upstream situation 
was rather unclear.

> The Fedora bug report that corresponds to strlcpy/strlcat
> fortification[2] says that this is "Priority low Severity low", and from
> the above it appears that this is mainly a packaging problem instead of
> being a necessary addition to the core libc API.

We did not invest in libbsd development because the right place to offer 
widely-implemented libc APIs in Fedora is glibc.

Florian
  
Florian Weimer June 24, 2016, 5:09 p.m. UTC | #5
On 05/18/2016 02:29 PM, Florian Weimer wrote:
> I would like to restart the discussion about inclusion of strlcpy and
> strlcat.
>
> The attached patch is basically what I had before, rebased to master.
> The symbol versions changed as a result.

I would like to move the discussion forward.  The patch is available here:

   <https://sourceware.org/ml/libc-alpha/2016-05/msg00369.html>

I think there remains very little technical work to be done on this 
initial implementation for glibc.

> Paul Sturm noticed an infinite recursion in the fortification wrappers.
> I fixed that an added a test case as well.

Since then, Paul Sturm has told me that the failures he saw are gone.

Thanks,
Florian
  
Guillem Jover Sept. 27, 2016, 3:59 a.m. UTC | #6
Hi!

[ Just noticed this now, upstream and Debian libbsd maintainer here. ]

On Mon, 2016-05-30 at 17:40:37 +0200, Florian Weimer wrote:
> On 05/20/2016 07:21 PM, Paul Eggert wrote:
> > First, the September 2014 thread motivated strlcpy/strlcat in part by
> > saying that Fedora has over 60 packages which define strlcpy/strlcat[1],
> > the implication being that moving strlcpy/strlcat into glibc would fix
> > more bugs than it would cause and would be worth the effort's cost.
> > Skeptics like me can reasonably counter that the effort would likely
> > just be security theater, in that it would not be of any real help but
> > would merely exhibit activity in the security area, and possibly would
> > even cause more problems than it fixes.

The intention with libbsd has always been to make porting easier, try
to reduce local implementations and embedded code copies, and have a
single place where those functions can be improved if need be, instead
of having to touch stuf all over the place. One additional advantage of
having it in libbsd is that it does not "pollute" more generally used
libraries, and allows to possibly migrate to it incrementally (in case
of locally diverging implementations), or possibly deprecate them
eventually. Although I've not yet had to bump the SOVERSION, and I'd
rather not do that if possible at all.

> I'm not sure if it is possible to easily quantify the security benefits that
> strlcpy/strlcat bring.  Any project we start in this area would prevent us
> from working directly on glibc improvements, due to the resource constraints
> we face.

I'm not going to get into whether any of the interfaces provided by
libbsd are good or not. They are provided because they are used, many
times by projects with very strong BSD origins, which will not stop
using them whether other people consider them bad or other systems
have those interfaces or not, and because the implementation embedded
in other projects are either out-dated, or of lower quality.

That's BTW probably one of the only projects for which I take such
"practical" stance.

> The major omissions are glibc and MSVC.  Most of the other libcs in the “no”
> camp are legacy implementations to various degrees.

Personally I've considered libbsd something that complements glibc,
providing interfaces that might not be suitable for the latter for
whatever reason. So, please feel free to send patches my way or
propose BSD-things that you might not be comfortable providing in glibc.

> > Second, the September 2014 thread said that another way to fortify
> > strlcpy/strlcat, assuming we still want to do it, would be to use
> > libbsd's strlcpy/strlcat implementation and add fortification to it.
> > However, it was argued that this would be a Fedora-only effort and that
> > libbsd contains functions like fgetln that are awkward. But isn't the
> > point of libbsd to provide BSD functions that are awkward or
> > questionable from the glibc point of view?
> 
> fgetln is not questionable at all by itself (except when reading lines from
> a read-only mapping or in-memory source, so that the non-const return value
> is incorrect).  Its awkwardness is the result of the implementation in
> libbsd, on top of stdio interface (and not libio internals).

Indeed, and that's a function libbsd inherited from its vestigial form
in the Debian GNU/kFreeBSD port. Which I've since marked as deprecated
in libbsd and made it warn to switch to getline(3).

And even if the fgetln() implementation in libbsd is "awkward" (which is
a very gentle way to put it), it is still the most robust and less buggy
implementation among all the many other embedded implementatoins I've
seen, given the public interfaces provided by glibc.

  <https://sourceware.org/ml/libc-alpha/2014-09/msg00357.html>
  <https://sourceware.org/ml/libc-alpha/2014-09/msg00424.html>

Florian, you mention you'd like to see libbsd disappear, due to lots of
other gross things in there. Given what I've seen around, I think that
would make the situation worse. I'd rather improve the stuff in libbsd,
and switch more projects to it. Besides fgetln() and fgetwln() what else
do you take issues with? I'm happy to fix, improve or deprecate things
that are broken, either by myself or by accepting patches!

> Once we have a clear picture of the vtable/ABI situation of libio, we can
> implement fgetln in glibc quite easily (with the caveat that writing to the
> returned buffer may not be allowed, depending on the underlying stream, but
> the BSDs have the same problem).  But that's a separate discussion.

I think pointing people to use getline(3), given that it's a
standardized function now is a better approach, and something that even
projects with strong BSD origins might be amenable to switch to.

But I'm happy to switch to anything that glibc provides that makes the
fgetln() implementation sane in libbsd. Although it will probably
still be very awkward to implement on other non-glibc systems that
lack a native implementation.

> > And I don't see how work in
> > libbsd would be Fedora-only, as other distributions like Debian also use
> > libbsd, and whatever methods Fedora uses to merge strlcpy/strlcat into
> > string.h would be methods that other distributions could use as well.

It is shipped in pretty much all major distributions now, AFAIK, see
the downstream list that I track at:

  <https://libbsd.freedesktop.org/wiki/>

> I might be misremembering things, but I think back in 2014, libbsd
> maintainership in Debian languished a bit, and the upstream situation was
> rather unclear.

Since its inception there's been at least a release per year, some
years have seen more activity. I've never received any enquiry about
the upstream or Debian status, so that comment is a bit surprising.

When I have time to scan for embedded code copies, I check for
"standard" BSD interfaces, and might add them if they can be implemented
somewhat sanely, if they seem useful or are used by several projects.

Given that I'd rather not diverge from the BSD interface behavior,
there's been usually not much incentive to do major code rewrites and
similar. At least not until the test suite has way more coverage. But
I'm happy to accept improvements and even rewrites (as long as they
preserve behavior) if that makes people more comfortable with the
implementations provided.

But all in all it's low-maintenance.

> > The Fedora bug report that corresponds to strlcpy/strlcat
> > fortification[2] says that this is "Priority low Severity low", and from
> > the above it appears that this is mainly a packaging problem instead of
> > being a necessary addition to the core libc API.
> 
> We did not invest in libbsd development because the right place to offer
> widely-implemented libc APIs in Fedora is glibc.

As Paul has mentioned, not all BSD interfaces might be acceptable for
glibc, and I think libbsd is the proper place for those.

Thanks,
Guillem
  
Carlos O'Donell Sept. 27, 2016, 7:24 p.m. UTC | #7
On 09/26/2016 11:59 PM, Guillem Jover wrote:
> Florian, you mention you'd like to see libbsd disappear, due to lots of
> other gross things in there. Given what I've seen around, I think that
> would make the situation worse. I'd rather improve the stuff in libbsd,
> and switch more projects to it. Besides fgetln() and fgetwln() what else
> do you take issues with? I'm happy to fix, improve or deprecate things
> that are broken, either by myself or by accepting patches!

I find the functions in libbsd are not well integrated into the kernel and
core libraries, and that leaves much to be desired from a QoI perspective,
so much so that other libraries sometimes roll their own because they don't
like the tradeoffs in libbsd. This has nothing to do with the quality of
libbsd itself, just that some things _can't_ be implemented or integrated
well in userspace. The most recent example of this I worked on was
setproctitle.

There should be no setproctitle in userspace, we need a kernel syscall to
support it properly, and once there is a syscall it's relatively easy to
add it to glibc given the infrastructure we have. We don't add it to glibc
because until we can actually implement it, the solutions I've seen do more
harm than good (debugging breakage due to env hacking) on Linux.

Once setproctitle is a syscall, then we _should_ implement a wrapper in glibc
(thought this follows on the Linux syscall wrapper consensus discussion).

>>> The Fedora bug report that corresponds to strlcpy/strlcat
>>> fortification[2] says that this is "Priority low Severity low", and from
>>> the above it appears that this is mainly a packaging problem instead of
>>> being a necessary addition to the core libc API.
>>
>> We did not invest in libbsd development because the right place to offer
>> widely-implemented libc APIs in Fedora is glibc.
> 
> As Paul has mentioned, not all BSD interfaces might be acceptable for
> glibc, and I think libbsd is the proper place for those.

This specific discussion is about strlcpy/strlcat, which I believe should
be implemented in libc proper because of their wide use.
  

Patch

2016-05-18  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.

2016-05-18  Florian Weimer  <fweimer@redhat.com>

	[BZ #178]
	* string/Makefile (routines): Add strlcpy, strlcat.
	(tests): Add tst-strlcpy, tst-strlcat.
	* string/Versions (2.23): 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.
	* manual/string.texi (Truncating Strings while Copying): Document
	strlcpy, strlcat.  Add reference to the strncpy documentation.
	* debug/Makefile (routines): Add strlcpy_chk, strlcat_chk.
	* debug/Versions (2.23): 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 b3fd3cc..22f8f73 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,9 @@  Version 2.24
   group: files [SUCCESS=merge] nis
   Implemented by Stephen Gallagher (Red Hat).
 
+* The GNU C Library now includes implementations of strlcpy and strlcat.
+  Contributed by Florian Weimer (Red Hat).
+
 Security related changes:
 
 * An unnecessary stack copy in _nss_dns_getnetbyname_r was removed.  It
diff --git a/debug/Makefile b/debug/Makefile
index 6b5f31e..6a915a7 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 0482c85..3b41c56 100644
--- a/debug/Versions
+++ b/debug/Versions
@@ -55,6 +55,10 @@  libc {
   GLIBC_2.16 {
     __poll_chk; __ppoll_chk;
   }
+  GLIBC_2.24 {
+    __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 4f968ee..a89070f 100644
--- a/debug/tst-chk1.c
+++ b/debug/tst-chk1.c
@@ -415,6 +415,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);
@@ -438,6 +448,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 e145bfd..3a0f73a 100644
--- a/include/string.h
+++ b/include/string.h
@@ -76,6 +76,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 016fd0b..9b340cd 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -1099,6 +1099,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 9c87419..5a404e9 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -41,7 +41,7 @@  routines	:= strcat strchr strcmp strcoll strcpy strcspn		\
 				     addsep replace)			\
 		   envz basename					\
 		   strcoll_l strxfrm_l string-inlines memrchr		\
-		   xpg-strerror strerror_l
+		   xpg-strerror strerror_l strlcpy strlcat
 
 strop-tests	:= memchr memcmp memcpy memmove mempcpy memset memccpy	\
 		   stpcpy stpncpy strcat strchr strcmp strcpy strcspn	\
@@ -54,7 +54,7 @@  tests		:= tester inl-tester noinl-tester testcopy test-ffs	\
 		   tst-strtok tst-strxfrm bug-strcoll1 tst-strfry	\
 		   bug-strtok1 $(addprefix test-,$(strop-tests))	\
 		   bug-envz1 tst-strxfrm2 tst-endian tst-svc2		\
-		   tst-strtok_r bug-strcoll2
+		   tst-strtok_r bug-strcoll2 tst-strlcpy tst-strlcat
 
 xtests = tst-strcoll-overflow
 
diff --git a/string/Versions b/string/Versions
index 475c1fd..278bb30 100644
--- a/string/Versions
+++ b/string/Versions
@@ -81,5 +81,7 @@  libc {
     strerror_l;
   }
   GLIBC_2.24 {
+    strlcpy;
+    strlcat;
   }
 }
diff --git a/string/bits/string3.h b/string/bits/string3.h
index dd8db68..5cfaf60 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
@@ -155,3 +157,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 c7f8fde..b8ff8db 100644
--- a/string/string.h
+++ b/string/string.h
@@ -574,6 +574,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/arm/nacl/libc.abilist b/sysdeps/arm/nacl/libc.abilist
index 0560510..42fbf0f 100644
--- a/sysdeps/arm/nacl/libc.abilist
+++ b/sysdeps/arm/nacl/libc.abilist
@@ -1840,3 +1840,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 5799239..77a0332 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2087,3 +2087,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 0fa4ee9..2ba6faf 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -1998,6 +1998,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 db9fa35..60cfc8a 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -88,6 +88,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 1d30644..215f080 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1852,6 +1852,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 8f3502d..578ed7a 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2010,6 +2010,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 921ec55..ba81926 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1874,6 +1874,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 019095b..ae84715 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -89,6 +89,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 a999a48..c73f50a 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1966,6 +1966,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 0a08bba..c930051 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2087,3 +2087,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 2ab9e94..e9acd95 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1941,6 +1941,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 b9b4b74..4cd8b58 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1939,6 +1939,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 14e1236..d3a2289 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1937,6 +1937,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 53e0c9a..1d0f132 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1932,6 +1932,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 dff1ee9..800805d 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2128,3 +2128,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 6861846..0b42579 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1970,6 +1970,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 fd611aa..58ae1b0 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1975,6 +1975,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index a97bd43..47eae10 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2175,3 +2175,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 00772cb..ea8e5b3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -89,6 +89,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 05cb85e..97fd60f 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1970,6 +1970,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 1af185f..a1bb3fd 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1871,6 +1871,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 e128692..4f3234b 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1856,6 +1856,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 eb14113..faa8a27 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1962,6 +1962,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 91b97ef..288c179 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1900,6 +1900,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 ffcc4a0..3150208 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2094,3 +2094,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 a66e8ec..7ea77ed 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2094,3 +2094,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index ffcc4a0..3150208 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2094,3 +2094,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 c6e3cd4..303e52b 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1851,6 +1851,11 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 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 04dc8e4..106a8e3 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2094,3 +2094,8 @@  GLIBC_2.23 fts64_close F
 GLIBC_2.23 fts64_open F
 GLIBC_2.23 fts64_read F
 GLIBC_2.23 fts64_set F
+GLIBC_2.24 GLIBC_2.24 A
+GLIBC_2.24 __strlcat_chk F
+GLIBC_2.24 __strlcpy_chk F
+GLIBC_2.24 strlcat F
+GLIBC_2.24 strlcpy F