[v3] Implement strlcpy [BZ #178]
diff mbox

Message ID 56326B79.8070804@redhat.com
State New
Headers show

Commit Message

Florian Weimer Oct. 29, 2015, 6:54 p.m. UTC
This version is v2, rebased to current master, with a change to the
documentation to refer to the appropriate place in the GNU Coding Standards.

The reasons why I want to add this to glibc are unchanged.  We can do
much better than the existing implementations:  This version is based on
optimized strlen and memcpy implementations (the defaults, not the
optimized versions).  It has fortify support.  In fortify mode, there
are link-time warnings for common misuses.

Commit message:

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

Florian

Comments

Joseph Myers Oct. 29, 2015, 7:01 p.m. UTC | #1
I support the addition of this API as sufficiently widely used in 
practice, but have not reviewed the substance of the patch.

> diff --git a/debug/strlcpy_chk.c b/debug/strlcpy_chk.c
> new file mode 100644
> index 0000000..038d8f9
> --- /dev/null
> +++ b/debug/strlcpy_chk.c
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2015 Free Software Foundation, Inc.

The first line of any new file should be a descriptive comment, before the 
copyright notice.

> diff --git a/string/strlcpy.c b/string/strlcpy.c
> new file mode 100644
> index 0000000..9b39d86
> --- /dev/null
> +++ b/string/strlcpy.c
> @@ -0,0 +1,46 @@
> +/* Copyright (C) 2015 Free Software Foundation, Inc.

Likewise.

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

Missing space before '('.

> diff --git a/string/tst-strlcpy.c b/string/tst-strlcpy.c
> new file mode 100644
> index 0000000..016411b
> --- /dev/null
> +++ b/string/tst-strlcpy.c

Missing descriptive comment.
Paul Eggert Oct. 29, 2015, 9:50 p.m. UTC | #2
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.

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

* 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 
less likely to break existing applications. It's true that POSIX and the 
C standard allow glibc to add str* names of all sorts, but it doesn't 
hurt to be realistic here about the user code that's undoubtedly out there.

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

> +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}."

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."
Paul Eggert Oct. 29, 2015, 9:53 p.m. UTC | #3
On 10/29/2015 02:50 PM, Paul Eggert wrote:
> 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." 

One more thing: we should add "it requires the source to be 
null-terminated even when it is longer than the destination".
Rich Felker Oct. 30, 2015, 4:49 p.m. UTC | #4
On Thu, Oct 29, 2015 at 02:50:54PM -0700, 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 think this needs to be explicit. There is a general principle,
which needs to be promoted rather than undermined, that a function
cannot have side effects/alter the value of objects outside of its
documented behavior.

> * 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'm not opposed to making a more visible warning, but the
documentation of the return value should make it clear. Requiring the
argument to point to "a string" also expresses the null termination
requirement. This is the same language used in the C standard for
string functions.

> * 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 less likely to break existing applications. It's true
> that POSIX and the C standard allow glibc to add str* names of all
> sorts, but it doesn't hurt to be realistic here about the user code
> that's undoubtedly out there.

The function should only be exposed at all when extensions are exposed
(_DEFAULT_SOURCE/_ALL_SOURCE) but __-prefixed functions should never
be a public API intended for applications to use. Also exposing the
function with a name different from widespread usage would not help
application portability.

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

No, the fortify version should just abort when this contract is
broken. Invalid usage contrary to documentation should not be silently
supported. This encourages non-portable programs which would break on
other strl* implementations.

Rich
Paul Eggert Oct. 31, 2015, 10:37 a.m. UTC | #5
Rich Felker wrote:

> There is a general principle,
> which needs to be promoted rather than undermined, that a function
> cannot have side effects/alter the value of objects outside of its
> documented behavior.

Regardless of whether that is an (undocumented?) general principle, this is a 
user manual not a formal standard.  In this particular context it's helpful to 
describe strlcpy's behavior carefully, since it's explained right after 
strncpy's and the reader might otherwise easily get confused into the 
misimpression that strlcpy behaves like strncpy except for guaranteeing 
null-termination when DESTLEN is nonzero.

> Requiring the
> argument to point to "a string" also expresses the null termination
> requirement. This is the same language used in the C standard for
> string functions.

Again, the point of the documentation is to be clear and useful, and it's 
helpful to spell things out given the strncpy context.  (Besides, this is a 
manual that contains phrases like "it is more efficient and works even if the 
string @var{s} is not null-terminated", so it's a bit much to expect the reader 
to know that "string" always means "null-terminated". :-)

> __-prefixed functions should never
> be a public API intended for applications to use.

Yes, that's fine.  My point was about the non-'__'-prefixed name, where it 
appears we agree.

> No, the fortify version should just abort when this contract is
> broken.

The idea was that this contract should be memmove-based, not memcpy-based, which 
means the fortify version would not abort if the arguments overlap. Still, I see 
your point: if we wanted move semantics, we'd have called the function 
'strlmove' or something like that. So I withdraw the suggestion to change the 
contract, and instead suggest that we add overlapping strings to our list of 
when not to use strlcpy, e.g,:

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, it requires the source to be null-terminated even when it is 
longer than the destination, it can crash if the source and destination overlap, 
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.


All in all, I'd still rather not add this poorly-designed API to the library.
Rich Felker Oct. 31, 2015, 8:24 p.m. UTC | #6
On Sat, Oct 31, 2015 at 03:37:10AM -0700, Paul Eggert wrote:
> Rich Felker wrote:
> 
> >There is a general principle,
> >which needs to be promoted rather than undermined, that a function
> >cannot have side effects/alter the value of objects outside of its
> >documented behavior.
> 
> Regardless of whether that is an (undocumented?) general principle,
> this is a user manual not a formal standard.  In this particular
> context it's helpful to describe strlcpy's behavior carefully, since
> it's explained right after strncpy's and the reader might otherwise
> easily get confused into the misimpression that strlcpy behaves like
> strncpy except for guaranteeing null-termination when DESTLEN is
> nonzero.

OK, I can agree with this.

> >Requiring the
> >argument to point to "a string" also expresses the null termination
> >requirement. This is the same language used in the C standard for
> >string functions.
> 
> Again, the point of the documentation is to be clear and useful, and
> it's helpful to spell things out given the strncpy context.
> (Besides, this is a manual that contains phrases like "it is more
> efficient and works even if the string @var{s} is not
> null-terminated", so it's a bit much to expect the reader to know
> that "string" always means "null-terminated". :-)

Likewise ok.

> >__-prefixed functions should never
> >be a public API intended for applications to use.
> 
> Yes, that's fine.  My point was about the non-'__'-prefixed name,
> where it appears we agree.
> 
> >No, the fortify version should just abort when this contract is
> >broken.
> 
> The idea was that this contract should be memmove-based, not

glibc's role here is not to design the contract but to implement the
existing contract precisely. This is because the whole goal of
providing these functions in glibc is to reduce subtle errors from
people reimplementing fallbacks in slightly-incompatible ways, not to
design a new API that we want to recommend people use.

> All in all, I'd still rather not add this poorly-designed API to the library.

Noted.

Rich
Roland McGrath Nov. 5, 2015, 9:11 p.m. UTC | #7
I still concur with Paul that we should not have this API in libc.
Rich Felker Nov. 6, 2015, 4:01 a.m. UTC | #8
On Thu, Nov 05, 2015 at 01:11:04PM -0800, Roland McGrath wrote:
> I still concur with Paul that we should not have this API in libc.

I think we're in agreement that it's not a "good API", but nonetheless
I see strong reasons for including them and no non-ideological reasons
not to. These have been covered in detail before so I won't repeat
them again unless you really want me to. Do you disagree with my
reasoning here?

Rich
Adhemerval Zanella Nov. 6, 2015, 12:09 p.m. UTC | #9
On 06-11-2015 02:01, Rich Felker wrote:
> On Thu, Nov 05, 2015 at 01:11:04PM -0800, Roland McGrath wrote:
>> I still concur with Paul that we should not have this API in libc.
> 
> I think we're in agreement that it's not a "good API", but nonetheless
> I see strong reasons for including them and no non-ideological reasons
> not to. These have been covered in detail before so I won't repeat
> them again unless you really want me to. Do you disagree with my
> reasoning here?
> 
> Rich
> 

Roland, I tend to agree with Paul also, but nonetheless any reasoning 
against this API is not refraining developers to continue reimplement 
and use this set of functions.
Tolga Dalman Nov. 8, 2015, 12:48 a.m. UTC | #10
On 11/06/2015 01:09 PM, Adhemerval Zanella wrote:
> 
> 
> On 06-11-2015 02:01, Rich Felker wrote:
>> On Thu, Nov 05, 2015 at 01:11:04PM -0800, Roland McGrath wrote:
>>> I still concur with Paul that we should not have this API in libc.
>>
>> I think we're in agreement that it's not a "good API", but nonetheless
>> I see strong reasons for including them and no non-ideological reasons
>> not to. These have been covered in detail before so I won't repeat
>> them again unless you really want me to. Do you disagree with my
>> reasoning here?
>>
>> Rich
>>
> 
> Roland, I tend to agree with Paul also, but nonetheless any reasoning 
> against this API is not refraining developers to continue reimplement 
> and use this set of functions.

My understanding is that there are several arguments for adding this API
(e.g., widely used in existing open source projects, performance,
specific existing use cases, portability) and against (e.g., encourages
possible security issues, additional maintenance,
similar-to-existing-API-but-yet-different-approach).

As a mere user, I have never needed strlcpy myself. Given that this
function is quite easy to realize, I see no reason to include such
a function in glibc. Also, I see only little value in improving the
portability with BSDs beyond POSIX, really. Thus, the real value of
adding this function appears to be convenience reasons. Anyway, this
is my personal view. As Rich stated, the arguments have been on the
table several times and it is not my intent to restart the discussion.

Instead, my proposal is to add this function to glibc, but in a separate library.
Also, I would like to see a comment like "Do not use this function unless
you really know what you do. Consider strncpy instead." in the documentation
(and man pages, eventually).

Thoughts?
Rich Felker Nov. 8, 2015, 1:07 a.m. UTC | #11
On Sun, Nov 08, 2015 at 01:48:38AM +0100, Tolga Dalman wrote:
> As a mere user, I have never needed strlcpy myself. Given that this
> function is quite easy to realize, I see no reason to include such
> a function in glibc.

Unfortunately that's NOT a given. Most reimplementations from scratch
have at least one serious corner-case bug. And if you have more than
one in the same program (e.g. multiple shared libraries that each
implement their own) they all end up using the copy from whichever
library was linked first, which might have a corner-case bug that
affects the other users. This is the main compelling security reason
for having the functions in libc: to get programs/libraries to stop
providing their own likely-buggy versions.

> Also, I see only little value in improving the
> portability with BSDs beyond POSIX, really. Thus, the real value of
> adding this function appears to be convenience reasons. Anyway, this
> is my personal view. As Rich stated, the arguments have been on the
> table several times and it is not my intent to restart the discussion.
> 
> Instead, my proposal is to add this function to glibc, but in a separate library.

That is utterly useless and much more costly. Each additional library
linked adds at least 4k of (non-shareable) memory usage to a process
and increases the start time by tens or hundreds of microseconds. But
most importantlu, if they're not available in the default link,
configure scripts will never find/use them, and they'll just end up
enabling their own replacements.

Anyway, in general, if adding a function to glibc is a bad idea,
adding it in its own separate .so file shipped with glibc is an even
worse idea. There are no advantages and lots of disadvantages.

Rich
Tolga Dalman Nov. 8, 2015, 1:41 a.m. UTC | #12
On 11/08/2015 02:07 AM, Rich Felker wrote:
> On Sun, Nov 08, 2015 at 01:48:38AM +0100, Tolga Dalman wrote:
>> As a mere user, I have never needed strlcpy myself. Given that this
>> function is quite easy to realize, I see no reason to include such
>> a function in glibc.
> 
> Unfortunately that's NOT a given. Most reimplementations from scratch
> have at least one serious corner-case bug. And if you have more than
> one in the same program (e.g. multiple shared libraries that each
> implement their own) they all end up using the copy from whichever
> library was linked first, which might have a corner-case bug that
> affects the other users. This is the main compelling security reason
> for having the functions in libc: to get programs/libraries to stop
> providing their own likely-buggy versions.

It is difficult to discuss about this topic in an abstract manner.
I believe that most uses of strlcpy and strlcat are really unjustified
as they could be easily replaced by strncat/strcpy. As I wrote above,
this is just my personal (perhaps naïve) view.
Can you send me a list of concrete examples where the specific features
of these functions are necessary (e.g., the memory beyond \0 must not
be written)?
If performance is really the reason, I would like to review some
benchmarks before continuing the discussion at that point.

>> Also, I see only little value in improving the
>> portability with BSDs beyond POSIX, really. Thus, the real value of
>> adding this function appears to be convenience reasons. Anyway, this
>> is my personal view. As Rich stated, the arguments have been on the
>> table several times and it is not my intent to restart the discussion.
>>
>> Instead, my proposal is to add this function to glibc, but in a separate library.
> 
> That is utterly useless and much more costly. Each additional library
> linked adds at least 4k of (non-shareable) memory usage to a process
> and increases the start time by tens or hundreds of microseconds. But
> most importantlu, if they're not available in the default link,
> configure scripts will never find/use them, and they'll just end up
> enabling their own replacements.
> 
> Anyway, in general, if adding a function to glibc is a bad idea,
> adding it in its own separate .so file shipped with glibc is an even
> worse idea. There are no advantages and lots of disadvantages.

I am not talking about a libstrlcpy but instead something like libbsd_ext.
Again, we are talking quite abstractly about applications. Do you have
a list of open source applications that use strlcpy or strlcat?

Anyway, I note that you are quick to argue against my idea. What is your
proposal to resolve the issue in glibc?
Rich Felker Nov. 8, 2015, 1:51 a.m. UTC | #13
On Sun, Nov 08, 2015 at 02:41:27AM +0100, Tolga Dalman wrote:
> On 11/08/2015 02:07 AM, Rich Felker wrote:
> > On Sun, Nov 08, 2015 at 01:48:38AM +0100, Tolga Dalman wrote:
> >> As a mere user, I have never needed strlcpy myself. Given that this
> >> function is quite easy to realize, I see no reason to include such
> >> a function in glibc.
> > 
> > Unfortunately that's NOT a given. Most reimplementations from scratch
> > have at least one serious corner-case bug. And if you have more than
> > one in the same program (e.g. multiple shared libraries that each
> > implement their own) they all end up using the copy from whichever
> > library was linked first, which might have a corner-case bug that
> > affects the other users. This is the main compelling security reason
> > for having the functions in libc: to get programs/libraries to stop
> > providing their own likely-buggy versions.
> 
> It is difficult to discuss about this topic in an abstract manner.
> I believe that most uses of strlcpy and strlcat are really unjustified
> as they could be easily replaced by strncat/strcpy. As I wrote above,

This is blatently wrong. strncpy is never a suitable repacement for
strlcpy. Its usage case is completely different: fixed-field-width,
non-null-terminated strings. These are largely obsolete (mainly used
in legacy on-disk data), and so is strncpy.

> this is just my personal (perhaps naïve) view.
> Can you send me a list of concrete examples where the specific features
> of these functions are necessary (e.g., the memory beyond \0 must not
> be written)?

That's not the main problem. Lack of null termination is a much bigger
problem. The biggest problem is that nobody understands it or knows
how to use it safely.

> If performance is really the reason, I would like to review some
> benchmarks before continuing the discussion at that point.

There were historically a number of programs which had really bad
performance because they were misusing strncpy for this purpose, with
very large buffers. I recall participating in the bug tracker tickets
for them, but I don't have links handy now. This was probably more
than a decade ago; people have mostly stopped the practice.

> >> Also, I see only little value in improving the
> >> portability with BSDs beyond POSIX, really. Thus, the real value of
> >> adding this function appears to be convenience reasons. Anyway, this
> >> is my personal view. As Rich stated, the arguments have been on the
> >> table several times and it is not my intent to restart the discussion.
> >>
> >> Instead, my proposal is to add this function to glibc, but in a separate library.
> > 
> > That is utterly useless and much more costly. Each additional library
> > linked adds at least 4k of (non-shareable) memory usage to a process
> > and increases the start time by tens or hundreds of microseconds. But
> > most importantlu, if they're not available in the default link,
> > configure scripts will never find/use them, and they'll just end up
> > enabling their own replacements.
> > 
> > Anyway, in general, if adding a function to glibc is a bad idea,
> > adding it in its own separate .so file shipped with glibc is an even
> > worse idea. There are no advantages and lots of disadvantages.
> 
> I am not talking about a libstrlcpy but instead something like libbsd_ext.
> Again, we are talking quite abstractly about applications. Do you have
> a list of open source applications that use strlcpy or strlcat?

Someone came up with one the last time this was discussed, I think.
You can search the libc-alpha archives.

> Anyway, I note that you are quick to argue against my idea. What is your
> proposal to resolve the issue in glibc?

My proposal has always been to implement it in glibc with the correct
semantics matching the original BSD function in all corner cases,
document it well, and note that it's not recommended but without
throwing too much ideology/propaganda into that note.

Rich
Joseph Myers Nov. 9, 2015, 12:59 p.m. UTC | #14
On Sat, 7 Nov 2015, Rich Felker wrote:

> Anyway, in general, if adding a function to glibc is a bad idea,
> adding it in its own separate .so file shipped with glibc is an even
> worse idea. There are no advantages and lots of disadvantages.

While it's trivially true that "adding a function to glibc is a bad idea" 
means it's bad to add it to any library in glibc, existing or new, there 
may well be cases where the conclusion is "it's a good idea to add this 
function to glibc, but in a new library (which might be might not be 
linked in automatically via AS_NEEDED in a .so linker script)".  Cf. 
libmvec or the libinux-syscalls.so.1 discussion.  It's just that I don't 
see strlcpy, strlcat or explicit_bzero as such cases - I see them as cases 
that belong in libc.
Rich Felker Nov. 9, 2015, 6:16 p.m. UTC | #15
On Mon, Nov 09, 2015 at 12:59:25PM +0000, Joseph Myers wrote:
> On Sat, 7 Nov 2015, Rich Felker wrote:
> 
> > Anyway, in general, if adding a function to glibc is a bad idea,
> > adding it in its own separate .so file shipped with glibc is an even
> > worse idea. There are no advantages and lots of disadvantages.
> 
> While it's trivially true that "adding a function to glibc is a bad idea" 
> means it's bad to add it to any library in glibc, existing or new, there 
> may well be cases where the conclusion is "it's a good idea to add this 
> function to glibc, but in a new library (which might be might not be 
> linked in automatically via AS_NEEDED in a .so linker script)".  Cf. 
> libmvec or the libinux-syscalls.so.1 discussion.  It's just that I don't 
> see strlcpy, strlcat or explicit_bzero as such cases - I see them as cases 
> that belong in libc.

Could you elaborate on when/how there would ever be a technical (not
ideologically-motivated) reason to add additional .so's as part of
glibc? I see all the ones we have (esp. libpthread and librt) as
historical mistakes that have been a perpetual source of problems
(like programs sticking with the deprecated gettimeofday instead of
clock_gettime because the latter needed -lrt, and broken weak symbol
hacks for pthreads like gcc pr57740, pr60662, or libxml2 pr704904)
that we should aim to eliminate, not duplicate.

Rich
Joseph Myers Nov. 9, 2015, 6:31 p.m. UTC | #16
On Mon, 9 Nov 2015, Rich Felker wrote:

> Could you elaborate on when/how there would ever be a technical (not
> ideologically-motivated) reason to add additional .so's as part of

Are you saying libinux-syscalls.so.1 is ideologically motivated?

libmvec is separate to allow for the possibility of using vector 
instructions that are only supported by binutils versions more recent than 
we wish to require as the minimum for a given architecture (this is a 
generic reason that could apply to multiple architectures) - the ABI 
provided by a library mustn't depend on the build tool version, but its 
presence or absence may.  (This reasoning does *not* work for powerpc 
float128 functions - quite clearly TS 18661-3 functions belong in 
libc/libm rather than another library, since many would be aliases of 
existing functions, so float128 functions can only be added to glibc for 
an architecture when there's consensus on a suitable minimum GCC version 
for building libc for that architecture.)

If libdfp were merged into glibc we'd need to consider carefully whether 
separate libraries or libc + libm were the way to go.  Cf. 
<https://gcc.gnu.org/ml/gcc-patches/2008-08/msg00870.html> regarding DFP 
support in libgcc being static-only to avoid making libgcc_s 8 times 
bigger for rarely used functionality (also raising concerns there about 
number of runtime relocations and size of writable segment, though without 
answers there).
Carlos O'Donell Nov. 9, 2015, 6:42 p.m. UTC | #17
On 11/09/2015 07:59 AM, Joseph Myers wrote:
> On Sat, 7 Nov 2015, Rich Felker wrote:
> 
>> Anyway, in general, if adding a function to glibc is a bad idea,
>> adding it in its own separate .so file shipped with glibc is an even
>> worse idea. There are no advantages and lots of disadvantages.
> 
> While it's trivially true that "adding a function to glibc is a bad idea" 
> means it's bad to add it to any library in glibc, existing or new, there 
> may well be cases where the conclusion is "it's a good idea to add this 
> function to glibc, but in a new library (which might be might not be 
> linked in automatically via AS_NEEDED in a .so linker script)".  Cf. 
> libmvec or the libinux-syscalls.so.1 discussion.  It's just that I don't 
> see strlcpy, strlcat or explicit_bzero as such cases - I see them as cases 
> that belong in libc.

I agree.

Cheers,
Carlos.
Rich Felker Nov. 9, 2015, 7:16 p.m. UTC | #18
On Mon, Nov 09, 2015 at 06:31:32PM +0000, Joseph Myers wrote:
> On Mon, 9 Nov 2015, Rich Felker wrote:
> 
> > Could you elaborate on when/how there would ever be a technical (not
> > ideologically-motivated) reason to add additional .so's as part of
> 
> Are you saying libinux-syscalls.so.1 is ideologically motivated?

Yes, so far all of the arguments I've seen for it have been that
somebody does not want them in libc.so because they're not the "GNU
API". I haven't seen any technical problem that's solved by putting
them in a separate .so, but of course there are technical problems
created (more memory usage and startup time, etc.).

> libmvec is separate to allow for the possibility of using vector 
> instructions that are only supported by binutils versions more recent than 
> we wish to require as the minimum for a given architecture (this is a 
> generic reason that could apply to multiple architectures) - the ABI 
> provided by a library mustn't depend on the build tool version, but its 
> presence or absence may.

This seems like a legitimate technical concern, but I don't think it's
the right solution. Instructions that aren't supported by the minimum
binutils version probably need to be represented with .byte or similar
until the version requirement is increased. IMO it's the whole
combined API/ABI of all the glibc libs, not just individual libs, that
should be independent of the tools used to build.

Rich
Rical Jasan Nov. 10, 2015, 6:59 a.m. UTC | #19
I don't have an opinion on strlcpy itself, but I've been following this
discussion and I think the best compromise I've heard is found in these
two statements:

On 11/07/2015 04:48 PM, Tolga Dalman wrote:
> Also, I would like to see a comment like "Do not use this function unless
> you really know what you do. Consider strncpy instead." in the documentation
> (and man pages, eventually).

On 11/07/2015 05:51 PM, Rich Felker wrote:
> My proposal has always been to implement it in glibc with the correct
> semantics matching the original BSD function in all corner cases,
> document it well, and note that it's not recommended but without
> throwing too much ideology/propaganda into that note.

As a glibc user, I take the manual at its word.  If it says to avoid use
of a function (and there are quite a few it says that about*), I will.
Rather than inclusion in glibc and subsequent blacklisting via the
manual though, I don't see any harm in providing documentation to the
effect of, "These are the appropriate use cases for this function;
otherwise it should be avoided."

I appreciate that some people want to provide what they feel is a safer
implementation of an error-prone routine.  Despite a pathological urge
to always reinvent the wheel, that is exactly one of the things I rely
on the providers of my C library (you all) to do.

Rical


* Some examples:
https://www.gnu.org/software/libc/manual/html_node/Charset-Function-Overview.html#Charset-Function-Overview
https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
https://www.gnu.org/software/libc/manual/html_node/Formatted-Output-Functions.html#Formatted-Output-Functions
Zack Weinberg Nov. 11, 2015, 2:20 a.m. UTC | #20
On 11/09/2015 01:16 PM, Rich Felker wrote:
> On Mon, Nov 09, 2015 at 12:59:25PM +0000, Joseph Myers wrote:
>> On Sat, 7 Nov 2015, Rich Felker wrote:
>>
>>> Anyway, in general, if adding a function to glibc is a bad idea,
>>> adding it in its own separate .so file shipped with glibc is an even
>>> worse idea. There are no advantages and lots of disadvantages.
>>
>> While it's trivially true that "adding a function to glibc is a bad idea" 
>> means it's bad to add it to any library in glibc, existing or new, there 
>> may well be cases where the conclusion is "it's a good idea to add this 
>> function to glibc, but in a new library (which might be might not be 
>> linked in automatically via AS_NEEDED in a .so linker script)".  Cf. 
>> libmvec or the libinux-syscalls.so.1 discussion.  It's just that I don't 
>> see strlcpy, strlcat or explicit_bzero as such cases - I see them as cases 
>> that belong in libc.
> 
> Could you elaborate on when/how there would ever be a technical (not
> ideologically-motivated) reason to add additional .so's as part of
> glibc? I see all the ones we have (esp. libpthread and librt) as
> historical mistakes that have been a perpetual source of problems
> (like programs sticking with the deprecated gettimeofday instead of
> clock_gettime because the latter needed -lrt, and broken weak symbol
> hacks for pthreads like gcc pr57740, pr60662, or libxml2 pr704904)
> that we should aim to eliminate, not duplicate.

FWIW, I tend to agree with this.

zw

Patch
diff mbox

2015-10-29  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
@@ -9,7 +9,7 @@  Version 2.23
 
 * The following bugs are resolved with this release:
 
-  887, 2542, 2543, 2558, 2898, 4404, 6803, 10432, 14341, 14912, 15367,
+  178, 887, 2542, 2543, 2558, 2898, 4404, 6803, 10432, 14341, 14912, 15367,
   15384, 15470, 15491, 15786, 15918, 16068, 16141, 16296, 16347, 16399,
   16415, 16422, 16517, 16519, 16520, 16521, 16620, 16734, 16973, 16985,
   17118, 17243, 17244, 17250, 17404, 17441, 17787, 17886, 17887, 17905,
@@ -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..038d8f9
--- /dev/null
+++ b/debug/strlcpy_chk.c
@@ -0,0 +1,29 @@ 
+/* 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..cc44201 100644
--- a/manual/string.texi
+++ b/manual/string.texi
@@ -576,17 +576,49 @@  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.
+@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 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..9b39d86
--- /dev/null
+++ b/string/strlcpy.c
@@ -0,0 +1,46 @@ 
+/* 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..016411b
--- /dev/null
+++ b/string/tst-strlcpy.c
@@ -0,0 +1,65 @@ 
+/* 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