diff mbox

[v4] Implement strlcpy [BZ #178]

Message ID 56376656.1000600@redhat.com
State New
Headers show

Commit Message

Florian Weimer Nov. 2, 2015, 1:34 p.m. UTC
On 10/29/2015 10:50 PM, Paul Eggert wrote:
> I'd rather we didn't add strlcpy to glibc, for reasons already
> discussed. If consensus goes against me, the patch still needs some
> work. I don't recall the details of our previous discussion; at the risk
> of reraising old issues here are some comments:
> 
> * If strlen(SRC) < DESTLEN, the documentation should clearly state that
> the contents of the bytes DEST[strlen(SRC) + 1] through DEST[DESTLEN -
> 1] are preserved.  The current documentation can be plausibly read that
> way, but it's not explicit.

I don't want to give any guarantees other implementations do not provide.

> * The proposed documentation can easily be misread as implying that
> strlcpy (DEST, SRC, DESTLEN) does O(DESTLEN) work, which is incorrect:
> strlcpy always does O(strlen(SRC)) work.  This point should be made
> clearly.  This is not merely a performance issue: it should be made
> crystal-clear that SRC must be null-terminated even if the source's
> trailing null byte is way after the bytes that strlcpy copies to DEST.

I added a sentence about null-termination just before the comment about
undefined behavior:

The string @var{from} must be null-terminated even if its length exceeds
that of the destination buffer.  The behavior of @code{strlcpy} is
undefined if the strings overlap or if the source or destination are
null pointers.

> * strlcpy's name should be prefixed by '__' by default. The names
> strlcpy and strlcpy_chk are both in the implementation namespace, and
> it's odd to have one without leading underscores and the other with
> them. I suggest a more cautious approach, in which both names are
> prefixed with '__' and unprefixed strlcpy is provided to the user only
> when GNU or BSD extensions are requested via _GNU_SOURCE etc.

This is what the patch always has done, the declaration is guarded by
__USE_MISC.

> This is less likely to break existing applications.

We'll see.  Compile-time breakage is one thing, but there could also be
incompatible interposed implementations.  For example, there are
implementations which specify int instead of size_t for the lengths.

> * strlcpy's implementation should use memmove instead of memcpy. The
> main motivations for strlcpy are safety and consistency and avoiding
> errors.  memmove obviously supports these goals better than memcpy
> does.  Efficiency is not a major concern with strlcpy (if it were,
> strlcpy wouldn't be O(strlen(SRC))).

The original OpenBSD implementation does not handle all overlapping
buffers in the same way memmove would.  Most BSDs now specify the
restrict qualify in the function prototype and documentation.

The existing *_chk functions do not check for overlapping inputs, and
they even have restrict pointer arguments, so such checks could be
futile anyway.  If we want to change this approach, I think this should
be a separate discussion.  (Right now, there is memstomp for this.)

>> +Not guaranteeing null termination and always overwriting the entire
>> +destination buffer makes @code{strncpy} rarely useful, but this behavior
>> +is specified by the @w{ISO C} standard.  See @code{strlcpy} below for an
>> +alternative.
> This quote is confusing, as it imples that strlcpy guarantees null
> termination, which strlcpy does not. I suggest rewording it to something
> like the following: "Often @code{strncpy} is not what you want, because
> it does not null-terminate the destination if the destination is smaller
> than the source, it always overwrites the entire destination buffer, it
> may truncate the destination, and it has undefined behavior if the
> source and destination overlap.  For alternatives, see the documentation
> below for @code{strlcpy}."

What about this?

Not guaranteeing null termination and always overwriting the entire
destination buffer makes @code{strncpy} rarely useful, but this behavior
is specified by the @w{ISO C} standard.  See @code{strlcpy} below for an
alternative which null-terminates the destination string as long
as the destination buffer does not have length zero.

> We can pair this with similar phrasing under strlcpy -- something like
> the following perhaps (this wording assumes strlcpy is changed to use
> memmove):"Often @code{strlcpy} is not what you want, because it does not
> null-terminate the destination if the destination's size is zero,it can
> leave junk data behind in the destination, it can do useless work when
> the source is long and the destination short, and it may truncate the
> destination.  Although one alternative is @code{strncpy}, it is usually
> better to use dynamic memory allocation and functions such as
> @code{strdup} or @code{asprintf} to construct strings."

The submitted patch already had this note directly following the bit you
criticized.

@strong{Note:} GNU programs should not use statically sized buffers for
storing strings.  @xref{Semantics, , Writing Robust Programs, standards,
The GNU Coding Standards}.  Instead of using @code{strlcpy}, it is
usually better to use dynamic memory allocation and functions such as
@code{strdup} or @code{asprintf} to construct strings.

I think this recommendation is clear enough.

Florian

Comments

Rich Felker Nov. 3, 2015, 4:15 p.m. UTC | #1
On Mon, Nov 02, 2015 at 02:34:14PM +0100, Florian Weimer wrote:
> On 10/29/2015 10:50 PM, Paul Eggert wrote:
> > I'd rather we didn't add strlcpy to glibc, for reasons already
> > discussed. If consensus goes against me, the patch still needs some
> > work. I don't recall the details of our previous discussion; at the risk
> > of reraising old issues here are some comments:
> > 
> > * If strlen(SRC) < DESTLEN, the documentation should clearly state that
> > the contents of the bytes DEST[strlen(SRC) + 1] through DEST[DESTLEN -
> > 1] are preserved.  The current documentation can be plausibly read that
> > way, but it's not explicit.
> 
> I don't want to give any guarantees other implementations do not provide.

I agree strongly with this principle, but I do think this is
guaranteed by other implementations, in terms of both actual behavior
and the implicit requirement not to clobber memory the function is not
specified to clobber. If there's doubt we should clarify with the
authors of the original. Am I correct in thinking the OpenBSD version
is the original?

> > * strlcpy's implementation should use memmove instead of memcpy. The
> > main motivations for strlcpy are safety and consistency and avoiding
> > errors.  memmove obviously supports these goals better than memcpy
> > does.  Efficiency is not a major concern with strlcpy (if it were,
> > strlcpy wouldn't be O(strlen(SRC))).
> 
> The original OpenBSD implementation does not handle all overlapping
> buffers in the same way memmove would.  Most BSDs now specify the
> restrict qualify in the function prototype and documentation.
> 
> The existing *_chk functions do not check for overlapping inputs, and
> they even have restrict pointer arguments, so such checks could be
> futile anyway.  If we want to change this approach, I think this should
> be a separate discussion.  (Right now, there is memstomp for this.)

Agreed, but I just want to add that I don't think the restrict
qualifier precludes testing for overlap. None of the requirements
imposed by restrict apply unless you actually access an object based
on the restrict-qualified pointer(s). So any tests that take place
before dereferencing should be safe against being optimized out.

Rich
Paul Eggert Nov. 3, 2015, 11:12 p.m. UTC | #2
On 11/03/2015 08:15 AM, Rich Felker wrote:
> I do think this is
> guaranteed by other implementations, in terms of both actual behavior
> and the implicit requirement not to clobber memory the function is not
> specified to clobber.

I don't see any such implicit requirement, so I agree with Florian here.

My impression all along has been that when a function is given an output 
buffer, it can write whatever it likes to the output buffer, so long as 
the buffer has the proper value when the function returns. In 
particular, the caller can't expect the buffer's irrelevant bytes (i.e., 
bytes not part of the output value) to be preserved. I see nothing in 
POSIX or in the BSD documentation that requires preservation of 
irrelevant bytes, either for strlcpy or for POSIX functions in general.  
So, for example, on POSIX implementations where struct timespec has 
padding bytes, a caller cannot expect clock_gettime(CLOCK_REALTIME, &ts) 
to preserve ts's padding bytes, because POSIX doesn't say irrelevant 
bytes are preserved.

> If there's doubt we should clarify with the authors of the original.

This is not an issue that OpenBSD implementers can unilaterally decide 
now. "Ooops, there's an nonobvious strlcpy feature that we forgot to 
write down, but all implementations are required to support it 
anyway."?!? Sorry, but no. That shouldn't work for clock_gettime, and it 
shouldn't work for strlcpy either.
Rich Felker Nov. 4, 2015, 12:56 a.m. UTC | #3
On Tue, Nov 03, 2015 at 03:12:47PM -0800, Paul Eggert wrote:
> On 11/03/2015 08:15 AM, Rich Felker wrote:
> >I do think this is
> >guaranteed by other implementations, in terms of both actual behavior
> >and the implicit requirement not to clobber memory the function is not
> >specified to clobber.
> 
> I don't see any such implicit requirement, so I agree with Florian here.

In that case, strlcpy can also assign environ=NULL and change the
disposition of all signal handlers to exec /usr/games/hack.

Jokes aside, a function that can have random side effects aside from
what it's specified to do is unusable.

> My impression all along has been that when a function is given an
> output buffer, it can write whatever it likes to the output buffer,
> so long as the buffer has the proper value when the function
> returns.

This is not the case, at least not for any of the standard functions.
Read the specifications. There is no text allowing such behavior.

> In particular, the caller can't expect the buffer's
> irrelevant bytes (i.e., bytes not part of the output value) to be
> preserved.

This is wrong, and the fact that it's wrong is actually essential to
some usage patterns like using fgets to read files that may contain
embedded nuls.

> I see nothing in POSIX or in the BSD documentation that
> requires preservation of irrelevant bytes, either for strlcpy or for
> POSIX functions in general.  So, for example, on POSIX
> implementations where struct timespec has padding bytes, a caller
> cannot expect clock_gettime(CLOCK_REALTIME, &ts) to preserve ts's
> padding bytes, because POSIX doesn't say irrelevant bytes are
> preserved.

Padding bytes are different; they're explicitly permitted to be
altered by any stores. Moreover, in the case of clock_gettime, the
specified behavior is storing a structure. This would allow any
implementation-defined additional members in the structure to be
changed since they are also part of the structure.

> >If there's doubt we should clarify with the authors of the original.
> 
> This is not an issue that OpenBSD implementers can unilaterally
> decide now. "Ooops, there's an nonobvious strlcpy feature that we
> forgot to write down, but all implementations are required to
> support it anyway."?!? Sorry, but no. That shouldn't work for
> clock_gettime, and it shouldn't work for strlcpy either.

It's not written down because it's obvious. If all implementations
satisfy the requirement (which they do, because it's obvious, and
you'd have to go out of your way to do something malicious in order
not to satisfy it) then there is no problem with clarifying this.

Rich
Paul Eggert Nov. 4, 2015, 2:50 a.m. UTC | #4
Rich Felker wrote:
> the fact that it's wrong is actually essential to
> some usage patterns like using fgets to read files that may contain
> embedded nuls.

Thanks, that's a usage pattern I wasn't familiar with.  However, among other 
things the glibc manual explicitly says that fgets shouldn't be used in that 
situation, which suggests the ice is quite thin here.  So even if POSIX says 
what you are asserting (I haven't seen specific citations, so I'm still 
skeptical), as a practical matter applications should not assume that irrelevant 
bytes in output buffers are preserved.

As far as strlcpy goes perhaps the manual should merely say that it's 
unspecified what happens to destination bytes after the trailing NUL.  We can 
always specify it later, should POSIX make things clear.  I doubt whether 
applications care much one way or another.
Rich Felker Nov. 4, 2015, 3:23 a.m. UTC | #5
On Tue, Nov 03, 2015 at 06:50:40PM -0800, Paul Eggert wrote:
> Rich Felker wrote:
> >the fact that it's wrong is actually essential to
> >some usage patterns like using fgets to read files that may contain
> >embedded nuls.
> 
> Thanks, that's a usage pattern I wasn't familiar with.  However,
> among other things the glibc manual explicitly says that fgets
> shouldn't be used in that situation, which suggests the ice is quite
> thin here.  So even if POSIX says what you are asserting (I haven't
> seen specific citations, so I'm still skeptical), as a practical
> matter applications should not assume that irrelevant bytes in
> output buffers are preserved.

Not even POSIX is needed for this; it's merely ISO C:

    "The fgets function reads at most one less than the number of
    characters specified by n from the stream pointed to by stream
    into the array pointed to by s. No additional characters are read
    after a new-line character (which is retained) or after
    end-of-file. A null character is written immediately after the
    last character read into the array."

n is merely a bound on the number of characters to be read; while the
buffer is usually at least n characters long, there's not even a
requirement that it need be; for situations where the application
controls the contents of a file, the behavior could be well-defined
even if the buffer is smaller than n.

> As far as strlcpy goes perhaps the manual should merely say that
> it's unspecified what happens to destination bytes after the
> trailing NUL.  We can always specify it later, should POSIX make
> things clear.  I doubt whether applications care much one way or
> another.

There's certainly existing code that relies on strcpy not clobbering
anything past the strlen(src)+1 bytes it writes. Naturally strcpy
_can't_ do this because it has no way of knowing the dest buffer size.
But to be able to use strlcpy as a safer drop-in replacement in such
code, it would need to preserve this property.

Rich
Florian Weimer Nov. 4, 2015, 1:11 p.m. UTC | #6
On 11/04/2015 04:23 AM, Rich Felker wrote:
> There's certainly existing code that relies on strcpy not clobbering
> anything past the strlen(src)+1 bytes it writes. Naturally strcpy
> _can't_ do this because it has no way of knowing the dest buffer size.
> But to be able to use strlcpy as a safer drop-in replacement in such
> code, it would need to preserve this property.

Good.  I'm now convinced that there is such a requirement for strcpy,
fgets and so on.  I checked our documentation and the standard for
strcpy and fgets, and we do not explicitly specify this behavior
(although I agree it is implicit).  My question is whether we should
specify this explicitly for strlcpy.  It would give the impression that
strlcpy is special in this regard, but it really is not.

Florian
Rich Felker Nov. 4, 2015, 3:08 p.m. UTC | #7
On Wed, Nov 04, 2015 at 02:11:25PM +0100, Florian Weimer wrote:
> On 11/04/2015 04:23 AM, Rich Felker wrote:
> > There's certainly existing code that relies on strcpy not clobbering
> > anything past the strlen(src)+1 bytes it writes. Naturally strcpy
> > _can't_ do this because it has no way of knowing the dest buffer size.
> > But to be able to use strlcpy as a safer drop-in replacement in such
> > code, it would need to preserve this property.
> 
> Good.  I'm now convinced that there is such a requirement for strcpy,
> fgets and so on.  I checked our documentation and the standard for
> strcpy and fgets, and we do not explicitly specify this behavior
> (although I agree it is implicit).  My question is whether we should
> specify this explicitly for strlcpy.  It would give the impression that
> strlcpy is special in this regard, but it really is not.

I'm fine with omitting explicit documentation (which I agree would
imply strlcpy is special and raise confusion about the general
principle) as long as there's consensus that this is part of the
interface contract. I'd like to see some general language added to the
glibc docs explaining this at some point.

Rich
Paul Eggert Nov. 4, 2015, 4:12 p.m. UTC | #8
On 11/04/2015 05:11 AM, Florian Weimer wrote:
> My question is whether we should
> specify this explicitly for strlcpy.  It would give the impression that
> strlcpy is special in this regard, but it really is not.
Not a problem, we can add a phrase to avoid giving that impression. 
Something like "Like other string-copying functions but unlike strncpy, 
strlcpy does not modify the part of the destination buffer that follows 
the trailing null byte."
Carlos O'Donell Nov. 10, 2015, 12:48 a.m. UTC | #9
On 11/04/2015 11:12 AM, Paul Eggert wrote:
> On 11/04/2015 05:11 AM, Florian Weimer wrote:
>> My question is whether we should specify this explicitly for
>> strlcpy.  It would give the impression that strlcpy is special in
>> this regard, but it really is not.
> Not a problem, we can add a phrase to avoid giving that impression.
> Something like "Like other string-copying functions but unlike
> strncpy, strlcpy does not modify the part of the destination buffer
> that follows the trailing null byte."

Agreed. The bytes that fall outside of the API contract should be
unmodified.

Doesn't this also have implications in concurrency?

Consider the remainder of @var{to} is being operated upon in parallel.
In the @code{race} section of intro.texi we talk briefly about how
users need to take care with memory buffers passed into functions,
but at the same time we must be exceedingly clear on our end what we
do with those buffers.

Should we forbid reads from parts of @var{to} that are not part of
the API contract?

Likewise there is also an API contract issue with reads of @var{from}
since it forbids concurrent writes to @var{from} because the
implementation has to compute the size of @var{from} by looking for
\0, and thus explicit locking has to be used to protect @var{from}
but not @var{to}, and this can't be easily fixed (follows logically
from the definition of the API).

Thoughts?

Cheers,
Carlos.
Carlos O'Donell Nov. 17, 2015, 5:58 a.m. UTC | #10
Florian,

My first question is: When are you going to implement strlcat? :-)

Thank you for implementing strlcpy. I think this and strlcat are
sufficiently used BSD APIs that we need them in glibc to avoid
duplication at the application. Worse those duplications may be
poorly implemented or have security flaws. Centralizing these in
glibc is the right way forward. Unlike C11's Annex K which was IMO
never well adopted and which has serious usability issues, the
strlcpy and strlcat APIs are better.

Overall this looks perfect. My only quibbles are that we should
be documenting better the invariants we are testing e.g. a few
more comments.

> 2015-11-02  Florian Weimer  <fweimer@redhat.com>
> 
> 	[BZ #178]
> 	* string/Makefile (routines): Add strlcpy.
> 	(tests): Add tst-strlcpy.
> 	* string/Versions (2.21): Export strlcpy.
> 	* string/strlcpy.c: New file.
> 	* string/tst-strlcpy.c: Likewise.
> 	* include/string.h (strlcpy): Declare as hidden.
> 	* manual/string.texi (Copying and Concatenation): Document
> 	strlcpy.  Update strncpy documentation.
> 	* debug/Makefile (routines): Add strlcpy_chk.
> 	* debug/Versions (2.21): Export __strlcpy_chk.
> 	* debug/tst-chk1.c (doit): Test strlcpy.
> 	* debug/strlcpy_chk.c: New file.
> 	* sysdeps/*/libc.abilist: Add strlcpy, __strlcpy_chk.
> 
> diff --git a/NEWS b/NEWS
> index 0831d35..af7e375 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -23,6 +23,8 @@ Version 2.23
>    19074, 19076, 19077, 19078, 19079, 19085, 19086, 19088, 19094, 19095,
>    19124, 19125, 19129, 19134, 19137, 19156, 19174, 19181.
>  
> +* The GNU C Library now includes an implementation of strlcpy.

Feel free to include a "Contributed by" here in the NEWS section.

e.g. Contributed by Florian Weimer (Red Hat).

It acknowledges the work done by the author and the corporate support.

> +
>  * A defect in the malloc implementation, present since glibc 2.15 (2012) or
>    glibc 2.10 via --enable-experimental-malloc (2009), could result in the
>    unnecessary serialization of memory allocation requests across threads.
> diff --git a/debug/Makefile b/debug/Makefile
> index a383417..f6cebab 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 \

OK.

>  	    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..60a7aed 100644
> --- a/debug/Versions
> +++ b/debug/Versions
> @@ -55,6 +55,9 @@ libc {
>    GLIBC_2.16 {
>      __poll_chk; __ppoll_chk;
>    }
> +  GLIBC_2.23 {
> +    __strlcpy_chk;
> +  }

OK.

>    GLIBC_PRIVATE {
>      __fortify_fail;
>    }
> diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
> new file mode 100644
> index 0000000..a13bcfe
> --- /dev/null
> +++ b/debug/strlcpy_chk.c
> @@ -0,0 +1,30 @@
> +/* Fortified version of strlcpy.
> +   Copyright (C) 2015 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>
> +
> +size_t
> +__strlcpy_chk (char *__restrict s1, const char *__restrict s2,
> +	       size_t n, size_t s1len)
> +{

Despite the simplicity I think this should have a comment about
the invariant being tested.

e.g.

/* The compiler determined size of the destination should not
   be smaller than the user provided size of the destination.  */

> +  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 bded583..0805a85 100644
> --- a/debug/tst-chk1.c
> +++ b/debug/tst-chk1.c
> @@ -415,6 +415,10 @@ 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

OK.

> +
>  # if !defined __cplusplus || defined __va_arg_pack
>    CHK_FAIL_START
>    sprintf (a.buf1 + (O + 7), "%d", num1);
> diff --git a/include/string.h b/include/string.h
> index a684fd9..82413af 100644
> --- a/include/string.h
> +++ b/include/string.h
> @@ -73,6 +73,7 @@ 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 (__rawmemchr)
>  libc_hidden_proto (__strcasecmp)
>  libc_hidden_proto (__strcasecmp_l)
> diff --git a/manual/string.texi b/manual/string.texi
> index 5f8a17e..8eda29a 100644
> --- a/manual/string.texi
> +++ b/manual/string.texi
> @@ -576,17 +576,52 @@ there is no null terminator written into @var{to}.
>  
>  If the length of @var{from} is less than @var{size}, then @code{strncpy}
>  copies all of @var{from}, followed by enough null characters to add up
> -to @var{size} characters in all.  This behavior is rarely useful, but it
> -is specified by the @w{ISO C} standard.
> +to @var{size} characters in all.

OK.

>  
>  The behavior of @code{strncpy} is undefined if the strings overlap.
>  
> -Using @code{strncpy} as opposed to @code{strcpy} is a way to avoid bugs
> +Not guaranteeing null termination and always overwriting the entire
> +destination buffer makes @code{strncpy} rarely useful, but this behavior
> +is specified by the @w{ISO C} standard.  See @code{strlcpy} below for an
> +alternative which null-terminates the destination string as long as the
> +destination buffer does not have length zero.
> +@end deftypefun

OK.

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

OK.

OpenBSD and FreeBSD document these as "@var{dst}" and "@var{src}" rather
than "@var{to}" and "@var{from}", but the rest of our manual use "to" and
"from" so I think this is fine.

> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +This function is similar to @code{strcpy}, but copies at most @var{size}
> +characters into @var{to}, including the terminating null character.
> +
> +If the length of @var{from} is equal to or more than @var{size}, then
> +@code{strlcpy} copies just the first @samp{@var{size} - 1} characters.

... and the null character?

> +As a special case, if @var{size} is zero, no bytes are written to
> +@var{to}.
> +
> +If the length of @var{from} is less than @var{size}, then @code{strlcpy}
> +copies all of @var{from}, followed by a single null character.
> +
> +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 string @var{from} must be null-terminated even if its length exceeds
> +that of the destination buffer.  The behavior of @code{strlcpy} is
> +undefined if the strings overlap or if the source or destination are
> +null pointers.
> +
> +Using @code{strlcpy} as opposed to @code{strcpy} is a way to avoid bugs
>  relating to writing past the end of the allocated space for @var{to}.
> -However, it can also make your program much slower in one common case:
> -copying a string which is probably small into a potentially large buffer.
> -In this case, @var{size} may be large, and when it is, @code{strncpy} will
> -waste a considerable amount of time copying null characters.
> +Unlike @code{strncpy}, @code{strlcpy} ensures that the destination
> +string is always null-terminated (unless the buffer size is zero), and
> +it does not fill the remaining part of the buffer with null characters.
> +
> +@strong{Note:} GNU programs should not use statically sized buffers for
> +storing strings.  @xref{Semantics, , Writing Robust Programs, standards,
> +The GNU Coding Standards}.  Instead of using @code{strlcpy}, it is
> +usually better to use dynamic memory allocation and functions such as
> +@code{strdup} or @code{asprintf} to construct strings.
>  @end deftypefun

OK.

>  
>  @comment wchar.h
> diff --git a/string/Makefile b/string/Makefile
> index ebe9354..0d1fb52 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

OK.

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

OK.

>  
>  xtests = tst-strcoll-overflow
>  
> diff --git a/string/Versions b/string/Versions
> index 59bf35a..bd00ca1 100644
> --- a/string/Versions
> +++ b/string/Versions
> @@ -80,4 +80,7 @@ libc {
>    GLIBC_2.6 {
>      strerror_l;
>    }
> +  GLIBC_2.23 {
> +    strlcpy;
> +  }

OK.

>  }
> diff --git a/string/bits/string3.h b/string/bits/string3.h
> index 4d11aa6..9f28a73 100644
> --- a/string/bits/string3.h
> +++ b/string/bits/string3.h
> @@ -40,6 +40,7 @@ __warndecl (__warn_memset_zero_len,
>  #  undef stpcpy
>  # endif
>  # ifdef __USE_MISC
> +#  undef strlcpy

OK.

>  #  undef bcopy
>  #  undef bzero
>  # endif
> @@ -155,3 +156,29 @@ __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;
> +
> +__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 ();

OK.

> +      return 0;
> +    }
> +  if (__builtin_constant_p (__len > __bos (__dest)) && __len > __bos (__dest))
> +    __warn_strlcpy_size_large ();

OK.

> +  if (__builtin_constant_p (__bos (__dest) == (size_t) -1)
> +      && __bos (__dest) == (size_t) -1)
> +    return strlcpy (__dest, __src, __len);
> +  return __strlcpy_chk (__dest, __src, __len, __bos (__dest));
> +}
> +#endif
> diff --git a/string/string.h b/string/string.h
> index 3ab7103..92a5d80 100644
> --- a/string/string.h
> +++ b/string/string.h
> @@ -574,6 +574,13 @@ extern char *stpncpy (char *__restrict __dest,
>       __THROW __nonnull ((1, 2));
>  #endif
>  
> +#ifdef __USE_MISC
> +/* Copy at most N characters from SRC to DEST.  */
> +extern size_t strlcpy (char *__restrict __dest,
> +		       const char *__restrict __src, size_t __n)
> +  __THROW __nonnull ((1, 2));
> +#endif
> +

OK.

>  #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/strlcpy.c b/string/strlcpy.c
> new file mode 100644
> index 0000000..3833a50
> --- /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) 2015 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;

OK.

> +}
> +libc_hidden_def (strlcpy)
> diff --git a/string/tst-strlcpy.c b/string/tst-strlcpy.c
> new file mode 100644
> index 0000000..b97f591
> --- /dev/null
> +++ b/string/tst-strlcpy.c
> @@ -0,0 +1,66 @@
> +/* Test the strlcpy function.
> +   Copyright (C) 2015 Free Software Foundation, Inc.

OK.

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

This needs more comments.

Each of these tests needs a comment explaining the intent of the test,
even a succinct comment is worth a lot to a future maintainer reviewing
the failed test.

> +  memset (&s, '@', sizeof (s));
> +  CHECK (strlcpy (s.buf1, "Hello!", 0) == 6);
> +  CHECK (memcmp (&s, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
> +
> +  memset (&s, '@', sizeof (s));
> +  CHECK (strlcpy (s.buf1, "Hello!", sizeof (s.buf1)) == 6);
> +  CHECK (memcmp (&s, "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
> +
> +  memset (&s, '@', sizeof (s));
> +  CHECK (strlcpy (s.buf1, "Hello, world!!!", sizeof (s.buf1)) == 15);
> +  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
> +		 sizeof (s)) == 0);
> +
> +  memset (&s, '@', sizeof (s));
> +  CHECK (strlcpy (s.buf1, "Hello, world!!!!", sizeof (s.buf1)) == 16);
> +  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
> +		 sizeof (s)) == 0);
> +
> +  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 561441e..9137c51 100644
> --- a/sysdeps/arm/nacl/libc.abilist
> +++ b/sysdeps/arm/nacl/libc.abilist
> @@ -1834,3 +1834,6 @@ GLIBC_2.22 wprintf F
>  GLIBC_2.22 write F
>  GLIBC_2.22 writev F
>  GLIBC_2.22 wscanf F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index c1ba62c..62c6412 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2081,3 +2081,6 @@ GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.18 _mcount F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 7d2d121..e5bff7e 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -1992,6 +1992,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 a30b258..5e7f8e9 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -82,6 +82,9 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 571548a..ff90320 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1846,6 +1846,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 87abbbd..ecf1d72 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2004,6 +2004,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 1cbdffe..8039f68 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1868,6 +1868,9 @@ GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.2.6 getunwind F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 061b5ee..8053211 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -83,6 +83,9 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 91be3d8..d3d9863 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1960,6 +1960,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 c1b1cad..6fe3aeb 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2081,3 +2081,6 @@ GLIBC_2.18 xprt_register F
>  GLIBC_2.18 xprt_unregister F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 3ef9b99..87de055 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1935,6 +1935,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 c44ee13..aed7e26 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1933,6 +1933,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 e8908e4..06d5c94 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1931,6 +1931,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 c6d1da2..e06e184 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1926,6 +1926,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 703979a..c837ec4 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2122,3 +2122,6 @@ GLIBC_2.21 xprt_register F
>  GLIBC_2.21 xprt_unregister F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 90ca484..866c0c5 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1964,6 +1964,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 a5ecc76..2b647f7 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1969,6 +1969,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 c6bea69..b0359b2 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2169,3 +2169,6 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 21e736b..df9a5f5 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -83,6 +83,9 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 d6c037b..12528d0 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1964,6 +1964,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 02422da..99d63b4 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1865,6 +1865,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 5accb42..5d7baaa 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1850,6 +1850,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 0ecba65..eda8cc2 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1956,6 +1956,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 ce45412..9c72aaf 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1894,6 +1894,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 63441e9..50bbb2d 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2088,3 +2088,6 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> index 212e4d5..51c87cd 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2088,3 +2088,6 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> index 63441e9..50bbb2d 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2088,3 +2088,6 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index afb29dc..60e85dc 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1845,6 +1845,9 @@ GLIBC_2.2.6 GLIBC_2.2.6 A
>  GLIBC_2.2.6 __nanosleep F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

>  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 7ac7104..386d4b3 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2088,3 +2088,6 @@ GLIBC_2.18 GLIBC_2.18 A
>  GLIBC_2.18 __cxa_thread_atexit_impl F
>  GLIBC_2.22 GLIBC_2.22 A
>  GLIBC_2.22 fmemopen F
> +GLIBC_2.23 GLIBC_2.23 A
> +GLIBC_2.23 __strlcpy_chk F
> +GLIBC_2.23 strlcpy F

OK.

Cheers,
Carlos.
diff mbox

Patch

2015-11-02  Florian Weimer  <fweimer@redhat.com>

	[BZ #178]
	* string/Makefile (routines): Add strlcpy.
	(tests): Add tst-strlcpy.
	* string/Versions (2.21): Export strlcpy.
	* string/strlcpy.c: New file.
	* string/tst-strlcpy.c: Likewise.
	* include/string.h (strlcpy): Declare as hidden.
	* manual/string.texi (Copying and Concatenation): Document
	strlcpy.  Update strncpy documentation.
	* debug/Makefile (routines): Add strlcpy_chk.
	* debug/Versions (2.21): Export __strlcpy_chk.
	* debug/tst-chk1.c (doit): Test strlcpy.
	* debug/strlcpy_chk.c: New file.
	* sysdeps/*/libc.abilist: Add strlcpy, __strlcpy_chk.

diff --git a/NEWS b/NEWS
index 0831d35..af7e375 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,8 @@  Version 2.23
   19074, 19076, 19077, 19078, 19079, 19085, 19086, 19088, 19094, 19095,
   19124, 19125, 19129, 19134, 19137, 19156, 19174, 19181.
 
+* The GNU C Library now includes an implementation of strlcpy.
+
 * A defect in the malloc implementation, present since glibc 2.15 (2012) or
   glibc 2.10 via --enable-experimental-malloc (2009), could result in the
   unnecessary serialization of memory allocation requests across threads.
diff --git a/debug/Makefile b/debug/Makefile
index a383417..f6cebab 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 \
 	    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..60a7aed 100644
--- a/debug/Versions
+++ b/debug/Versions
@@ -55,6 +55,9 @@  libc {
   GLIBC_2.16 {
     __poll_chk; __ppoll_chk;
   }
+  GLIBC_2.23 {
+    __strlcpy_chk;
+  }
   GLIBC_PRIVATE {
     __fortify_fail;
   }
diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
new file mode 100644
index 0000000..a13bcfe
--- /dev/null
+++ b/debug/strlcpy_chk.c
@@ -0,0 +1,30 @@ 
+/* Fortified version of strlcpy.
+   Copyright (C) 2015 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>
+
+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 bded583..0805a85 100644
--- a/debug/tst-chk1.c
+++ b/debug/tst-chk1.c
@@ -415,6 +415,10 @@  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
+
 # if !defined __cplusplus || defined __va_arg_pack
   CHK_FAIL_START
   sprintf (a.buf1 + (O + 7), "%d", num1);
diff --git a/include/string.h b/include/string.h
index a684fd9..82413af 100644
--- a/include/string.h
+++ b/include/string.h
@@ -73,6 +73,7 @@  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 (__rawmemchr)
 libc_hidden_proto (__strcasecmp)
 libc_hidden_proto (__strcasecmp_l)
diff --git a/manual/string.texi b/manual/string.texi
index 5f8a17e..8eda29a 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -576,17 +576,52 @@  there is no null terminator written into @var{to}.
 
 If the length of @var{from} is less than @var{size}, then @code{strncpy}
 copies all of @var{from}, followed by enough null characters to add up
-to @var{size} characters in all.  This behavior is rarely useful, but it
-is specified by the @w{ISO C} standard.
+to @var{size} characters in all.
 
 The behavior of @code{strncpy} is undefined if the strings overlap.
 
-Using @code{strncpy} as opposed to @code{strcpy} is a way to avoid bugs
+Not guaranteeing null termination and always overwriting the entire
+destination buffer makes @code{strncpy} rarely useful, but this behavior
+is specified by the @w{ISO C} standard.  See @code{strlcpy} below for an
+alternative which null-terminates the destination string as long as the
+destination buffer does not have length zero.
+@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}
+characters into @var{to}, including the terminating null character.
+
+If the length of @var{from} is equal to or more than @var{size}, then
+@code{strlcpy} copies just the first @samp{@var{size} - 1} characters.
+As a special case, if @var{size} is zero, no bytes are written to
+@var{to}.
+
+If the length of @var{from} is less than @var{size}, then @code{strlcpy}
+copies all of @var{from}, followed by a single null character.
+
+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 string @var{from} must be null-terminated even if its length exceeds
+that of the destination buffer.  The behavior of @code{strlcpy} is
+undefined if the strings overlap or if the source or destination are
+null pointers.
+
+Using @code{strlcpy} as opposed to @code{strcpy} is a way to avoid bugs
 relating to writing past the end of the allocated space for @var{to}.
-However, it can also make your program much slower in one common case:
-copying a string which is probably small into a potentially large buffer.
-In this case, @var{size} may be large, and when it is, @code{strncpy} will
-waste a considerable amount of time copying null characters.
+Unlike @code{strncpy}, @code{strlcpy} ensures that the destination
+string is always null-terminated (unless the buffer size is zero), and
+it does not fill the remaining part of the buffer with null characters.
+
+@strong{Note:} GNU programs should not use statically sized buffers for
+storing strings.  @xref{Semantics, , Writing Robust Programs, standards,
+The GNU Coding Standards}.  Instead of using @code{strlcpy}, it is
+usually better to use dynamic memory allocation and functions such as
+@code{strdup} or @code{asprintf} to construct strings.
 @end deftypefun
 
 @comment wchar.h
diff --git a/string/Makefile b/string/Makefile
index ebe9354..0d1fb52 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
 
 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
 
 xtests = tst-strcoll-overflow
 
diff --git a/string/Versions b/string/Versions
index 59bf35a..bd00ca1 100644
--- a/string/Versions
+++ b/string/Versions
@@ -80,4 +80,7 @@  libc {
   GLIBC_2.6 {
     strerror_l;
   }
+  GLIBC_2.23 {
+    strlcpy;
+  }
 }
diff --git a/string/bits/string3.h b/string/bits/string3.h
index 4d11aa6..9f28a73 100644
--- a/string/bits/string3.h
+++ b/string/bits/string3.h
@@ -40,6 +40,7 @@  __warndecl (__warn_memset_zero_len,
 #  undef stpcpy
 # endif
 # ifdef __USE_MISC
+#  undef strlcpy
 #  undef bcopy
 #  undef bzero
 # endif
@@ -155,3 +156,29 @@  __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;
+
+__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 (__dest, __src, __len);
+  return __strlcpy_chk (__dest, __src, __len, __bos (__dest));
+}
+#endif
diff --git a/string/string.h b/string/string.h
index 3ab7103..92a5d80 100644
--- a/string/string.h
+++ b/string/string.h
@@ -574,6 +574,13 @@  extern char *stpncpy (char *__restrict __dest,
      __THROW __nonnull ((1, 2));
 #endif
 
+#ifdef __USE_MISC
+/* Copy at most N characters from SRC to DEST.  */
+extern size_t strlcpy (char *__restrict __dest,
+		       const char *__restrict __src, size_t __n)
+  __THROW __nonnull ((1, 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/strlcpy.c b/string/strlcpy.c
new file mode 100644
index 0000000..3833a50
--- /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) 2015 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-strlcpy.c b/string/tst-strlcpy.c
new file mode 100644
index 0000000..b97f591
--- /dev/null
+++ b/string/tst-strlcpy.c
@@ -0,0 +1,66 @@ 
+/* Test the strlcpy function.
+   Copyright (C) 2015 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;
+
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello!", 0) == 6);
+  CHECK (memcmp (&s, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello!", sizeof (s.buf1)) == 6);
+  CHECK (memcmp (&s, "Hello!\0@@@@@@@@@@@@@@@@@@@@@@@@@", sizeof (s)) == 0);
+
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello, world!!!", sizeof (s.buf1)) == 15);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  memset (&s, '@', sizeof (s));
+  CHECK (strlcpy (s.buf1, "Hello, world!!!!", sizeof (s.buf1)) == 16);
+  CHECK (memcmp (&s, "Hello, world!!!\0@@@@@@@@@@@@@@@@@@@@@@@@@",
+		 sizeof (s)) == 0);
+
+  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 561441e..9137c51 100644
--- a/sysdeps/arm/nacl/libc.abilist
+++ b/sysdeps/arm/nacl/libc.abilist
@@ -1834,3 +1834,6 @@  GLIBC_2.22 wprintf F
 GLIBC_2.22 write F
 GLIBC_2.22 writev F
 GLIBC_2.22 wscanf F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index c1ba62c..62c6412 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2081,3 +2081,6 @@  GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.18 _mcount F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 7d2d121..e5bff7e 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -1992,6 +1992,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 a30b258..5e7f8e9 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -82,6 +82,9 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 571548a..ff90320 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1846,6 +1846,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 87abbbd..ecf1d72 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2004,6 +2004,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 1cbdffe..8039f68 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1868,6 +1868,9 @@  GLIBC_2.2.6 __nanosleep F
 GLIBC_2.2.6 getunwind F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 061b5ee..8053211 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -83,6 +83,9 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 91be3d8..d3d9863 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1960,6 +1960,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 c1b1cad..6fe3aeb 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2081,3 +2081,6 @@  GLIBC_2.18 xprt_register F
 GLIBC_2.18 xprt_unregister F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 3ef9b99..87de055 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1935,6 +1935,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 c44ee13..aed7e26 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1933,6 +1933,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 e8908e4..06d5c94 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1931,6 +1931,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 c6d1da2..e06e184 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1926,6 +1926,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 703979a..c837ec4 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2122,3 +2122,6 @@  GLIBC_2.21 xprt_register F
 GLIBC_2.21 xprt_unregister F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 90ca484..866c0c5 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1964,6 +1964,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 a5ecc76..2b647f7 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1969,6 +1969,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 c6bea69..b0359b2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2169,3 +2169,6 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 21e736b..df9a5f5 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -83,6 +83,9 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 d6c037b..12528d0 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1964,6 +1964,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 02422da..99d63b4 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1865,6 +1865,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 5accb42..5d7baaa 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1850,6 +1850,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 0ecba65..eda8cc2 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1956,6 +1956,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 ce45412..9c72aaf 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1894,6 +1894,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 63441e9..50bbb2d 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2088,3 +2088,6 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 212e4d5..51c87cd 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2088,3 +2088,6 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 strlcpy F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index 63441e9..50bbb2d 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2088,3 +2088,6 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 afb29dc..60e85dc 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1845,6 +1845,9 @@  GLIBC_2.2.6 GLIBC_2.2.6 A
 GLIBC_2.2.6 __nanosleep F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 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 7ac7104..386d4b3 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2088,3 +2088,6 @@  GLIBC_2.18 GLIBC_2.18 A
 GLIBC_2.18 __cxa_thread_atexit_impl F
 GLIBC_2.22 GLIBC_2.22 A
 GLIBC_2.22 fmemopen F
+GLIBC_2.23 GLIBC_2.23 A
+GLIBC_2.23 __strlcpy_chk F
+GLIBC_2.23 strlcpy F