Skip to content

Compiling C program with zig cc with FORTIFY_SOURCE enabled leads to linking issue when building against < 2.38 glibc version #21252

Closed
@FedeDP

Description

@FedeDP

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavior

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions