Description
Zig Version
0.13.0
Steps to Reproduce and Observed Behavior
Source file:
#include <string.h>
int main(void) {
char s[10]; strlcpy(s, "foo", sizeof(s));
return 0;
}
zig cc test.c -o test -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -O2
leads to OK compilation (using glibc 2.38).
Targeting instead eg: glibc 2.17 leads to issue:
zig cc -target x86_64-linux-gnu.2.17 test.c -o test -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -O2
ld.lld: error: undefined symbol: __strlcpy_chk
>>> referenced by string_fortified.h:156 (zig/zig-linux-x86_64-0.13.0/lib/libc/include/generic-glibc/bits/string_fortified.h:156)
>>> test.lto.o:(main)
>>> did you mean: __strncpy_chk
>>> defined in: /home/federico/.cache/zig/o/6d6ed7dfa74bdd6b9de1e0f01ec3621d/libc.so.6
But the strlcpy
symbol is actually found (it complains that strlcpy
calls __strncpy_chk
that is not defined).
This is hitting us because, as many projects, we have a cmake check around strlcpy
, like:
include(CheckSymbolExists)
check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY)
and this is failing when building against glibc 2.17, of course; follows that our own internal strlcpy
implementation gets compiled, but <string.h>
header still exposes the strlcpy
prototype, leading to a redefinition error.
Relevant bits:
<string.h>
:
#if __GNUC_PREREQ (3,4)
# if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
/* Functions with security checks. */
# include <bits/string_fortified.h>
# endif
#endif
<bits/string_fortified.h>
extern size_t __strlcpy_chk (char *__dest, const char *__src, size_t __n,
size_t __destlen) __THROW;
extern size_t __REDIRECT_NTH (__strlcpy_alias,
(char *__dest, const char *__src, size_t __n),
strlcpy);
__fortify_function size_t
__NTH (strlcpy (char *__restrict __dest, const char *__restrict __src,
size_t __n))
{
if (__glibc_objsize (__dest) != (size_t) -1
&& (!__builtin_constant_p (__n > __glibc_objsize (__dest))
|| __n > __glibc_objsize (__dest)))
return __strlcpy_chk (__dest, __src, __n, __glibc_objsize (__dest));
return __strlcpy_alias (__dest, __src, __n);
}
My (super wild) guess is that the declaration in string_fortified.h
must still be surrounded by the
#if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 38) || __GLIBC__ > 2
check.
Indeed, patching <bits/string_fortified.h>
adding the surrounding guard, properly gives the expected error:
zig cc -target x86_64-linux-gnu.2.17 test.c -o test -D_FORTIFY_SOURCE=2 -O2 -D_GNU_SOURCE
test.c:3:15: error: call to undeclared library function 'strlcpy' with type 'unsigned long (char *, const char *, unsigned long)'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
3 | char s[10]; strlcpy(s, "foo", sizeof(s));
| ^
test.c:3:15: note: include the header <string.h> or explicitly provide a declaration for 'strlcpy'
1 error generated.
since strlcpy
was added to glibc 2.38.
Expected Behavior
I'd expect that no strlcpy
/ strlcat
prototype is included via <string.h>
on glibc <2.38 versions, since they were not existing, even with _FORTIFY_SOURCE
enabled.