stpncpy has the same bug as strncpy, on FreeBSD 15.0. This patch adds a workaround.
2025-12-02 Bruno Haible <[email protected]> tests: Update an old manual test. * tests/test-stpncpy.c (main): Fix printf call on 64-bit platforms. 2025-12-02 Bruno Haible <[email protected]> stpncpy: Add workaround for FreeBSD 15.0/x86_64. * m4/stpncpy.m4 (gl_FUNC_STPNCPY): Check against FreeBSD bug, using code taken from m4/strncpy.m4. * modules/stpncpy (Files): Add m4/mmap-anon.m4. * doc/posix-functions/stpncpy.texi: Mention the FreeBSD bug.
>From 216d1de5407eb7de5165b469a870f4acd9f7c986 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Wed, 3 Dec 2025 00:06:14 +0100 Subject: [PATCH 1/2] stpncpy: Add workaround for FreeBSD 15.0/x86_64. * m4/stpncpy.m4 (gl_FUNC_STPNCPY): Check against FreeBSD bug, using code taken from m4/strncpy.m4. * modules/stpncpy (Files): Add m4/mmap-anon.m4. * doc/posix-functions/stpncpy.texi: Mention the FreeBSD bug. --- ChangeLog | 8 ++++ doc/posix-functions/stpncpy.texi | 4 ++ m4/stpncpy.m4 | 68 +++++++++++++++++++++++++++++--- modules/stpncpy | 1 + 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1e727e6ba3..8ca5a613b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2025-12-02 Bruno Haible <[email protected]> + + stpncpy: Add workaround for FreeBSD 15.0/x86_64. + * m4/stpncpy.m4 (gl_FUNC_STPNCPY): Check against FreeBSD bug, using code + taken from m4/strncpy.m4. + * modules/stpncpy (Files): Add m4/mmap-anon.m4. + * doc/posix-functions/stpncpy.texi: Mention the FreeBSD bug. + 2025-12-02 Bruno Haible <[email protected]> Make use of the strncpy module. diff --git a/doc/posix-functions/stpncpy.texi b/doc/posix-functions/stpncpy.texi index 62ade9c410..0dcafefd9e 100644 --- a/doc/posix-functions/stpncpy.texi +++ b/doc/posix-functions/stpncpy.texi @@ -19,6 +19,10 @@ This function cannot be called from plain inline or extern inline functions on some platforms: macOS 10.8. +@item +This function looks are more bytes of the source string than allowed +on some platforms: +FreeBSD 15.0/x86_64. @end itemize Portability problems not fixed by Gnulib: diff --git a/m4/stpncpy.m4 b/m4/stpncpy.m4 index 196e4898ca..7028bc88e7 100644 --- a/m4/stpncpy.m4 +++ b/m4/stpncpy.m4 @@ -1,7 +1,6 @@ # stpncpy.m4 -# serial 22 -dnl Copyright (C) 2002-2003, 2005-2007, 2009-2025 Free Software Foundation, -dnl Inc. +# serial 23 +dnl Copyright (C) 2002-2007, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -19,6 +18,11 @@ AC_DEFUN([gl_FUNC_STPNCPY] AC_REQUIRE([gl_STRING_H_DEFAULTS]) + dnl Check for prerequisites for memory fence checks. + gl_FUNC_MMAP_ANON + AC_CHECK_HEADERS_ONCE([sys/mman.h]) + AC_CHECK_FUNCS_ONCE([mprotect]) + dnl Both glibc and AIX (4.3.3, 5.1) have an stpncpy() function dnl declared in <string.h>. Its side effects are the same as those dnl of strncpy(): @@ -28,6 +32,11 @@ AC_DEFUN([gl_FUNC_STPNCPY] dnl in glibc: dest + min(strlen(src),n) dnl in AIX: dest + max(0,n-1) dnl Only the glibc return value is useful in practice. + dnl + dnl Also detect bug in FreeBSD 15.0 on x86_64: + dnl stpncpy should not dereference more than n bytes, but always dereferences + dnl n+1 bytes if the first n bytes don't contain a NUL byte. + dnl Assume that stpncpy works on platforms that lack mprotect. AC_CHECK_DECLS_ONCE([stpncpy]) gl_CHECK_FUNCS_ANDROID([stpncpy], [[#include <string.h>]]) @@ -45,6 +54,12 @@ AC_DEFUN([gl_FUNC_STPNCPY] # endif char *stpncpy (char *dest, const char *src, size_t n); #endif +#if HAVE_SYS_MMAN_H +# include <fcntl.h> +# include <unistd.h> +# include <sys/types.h> +# include <sys/mman.h> +#endif int main () { int result = 0; @@ -68,12 +83,52 @@ AC_DEFUN([gl_FUNC_STPNCPY] if (stpncpy (dest, src, 7) != dest + 5) result |= 4; } + /* FreeBSD 15.0/x86_64 crashes here. */ + { + char *fence = NULL; +#if HAVE_SYS_MMAN_H && HAVE_MPROTECT + { + long int pagesize = sysconf (_SC_PAGESIZE); + char *two_pages = + (char *) mmap (NULL, 2 * pagesize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (two_pages != (char *)(-1) + && mprotect (two_pages + pagesize, pagesize, PROT_NONE) == 0) + fence = two_pages + pagesize; + } +#endif + if (fence) + { + char dest[8]; + + dest[0] = 'a'; + dest[1] = 'b'; + dest[2] = 'c'; + dest[3] = 'd'; + dest[4] = 'e'; + dest[5] = 'f'; + dest[6] = 'g'; + + *(fence - 3) = '7'; + *(fence - 2) = '2'; + *(fence - 1) = '9'; + + if (stpncpy (dest + 1, fence - 3, 3) != dest + 4) + result |= 8; + if (dest[0] != 'a') + result |= 16; + if (dest[1] != '7' || dest[2] != '2' || dest[3] != '9') + result |= 32; + if (dest[4] != 'e') + result |= 16; + } + } return result; } ]])], [gl_cv_func_stpncpy=yes], [gl_cv_func_stpncpy=no], - [dnl Guess yes on glibc systems and musl systems. + [dnl Guess yes on glibc systems and musl systems, no on FreeBSD. AC_EGREP_CPP([Thanks for using GNU], [ #include <features.h> #ifdef __GNU_LIBRARY__ @@ -81,8 +136,9 @@ AC_DEFUN([gl_FUNC_STPNCPY] #endif ], [gl_cv_func_stpncpy="guessing yes"], [case "$host_os" in - *-musl* | midipix*) gl_cv_func_stpncpy="guessing yes" ;; - *) gl_cv_func_stpncpy="$gl_cross_guess_normal" ;; + *-musl* | midipix*) gl_cv_func_stpncpy="guessing yes" ;; + freebsd* | dragonfly*) gl_cv_func_stpncpy="guessing no" ;; + *) gl_cv_func_stpncpy="$gl_cross_guess_normal" ;; esac ]) ]) diff --git a/modules/stpncpy b/modules/stpncpy index c1093474d5..bb02f3236a 100644 --- a/modules/stpncpy +++ b/modules/stpncpy @@ -4,6 +4,7 @@ stpncpy() function: copy a size-bounded string, returning a pointer to its end. Files: lib/stpncpy.c m4/stpncpy.m4 +m4/mmap-anon.m4 Depends-on: extensions -- 2.51.0
>From f3e370b6224ed24308ca1ad0185741ae45bad13f Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Wed, 3 Dec 2025 00:07:21 +0100 Subject: [PATCH 2/2] tests: Update an old manual test. * tests/test-stpncpy.c (main): Fix printf call on 64-bit platforms. --- ChangeLog | 5 +++++ tests/test-stpncpy.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 8ca5a613b4..14cb5270a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-12-02 Bruno Haible <[email protected]> + + tests: Update an old manual test. + * tests/test-stpncpy.c (main): Fix printf call on 64-bit platforms. + 2025-12-02 Bruno Haible <[email protected]> stpncpy: Add workaround for FreeBSD 15.0/x86_64. diff --git a/tests/test-stpncpy.c b/tests/test-stpncpy.c index 6606b53116..c398da939c 100644 --- a/tests/test-stpncpy.c +++ b/tests/test-stpncpy.c @@ -53,7 +53,7 @@ main () putchar (to[h]); else printf ("\\x%02x", to[h]); - printf (" ret = to + %d\n", ret - to); + printf (" ret = to + %zd\n", ret - to); } return 0; -- 2.51.0
