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

Reply via email to