summaryrefslogtreecommitdiff
path: root/string
diff options
context:
space:
mode:
authorJoseph Myers <josmyers@redhat.com>2025-10-01 15:14:09 +0000
committerJoseph Myers <josmyers@redhat.com>2025-10-01 15:14:09 +0000
commit0f201f4a817e39c01c502f523d4ea3c91f242767 (patch)
tree363f693dbff6fd086faf1bfd89e014346837436e /string
parenta8ad2e9e431bac3ea207be07c64cddb72c290cde (diff)
Implement C23 memset_explicit (bug 32378)
Add the C23 memset_explicit function to glibc. Everything here is closely based on the approach taken for explicit_bzero. This includes the bits that relate to internal uses of explicit_bzero within glibc (although we don't currently have any such internal uses of memset_explicit), and also includes the nonnull attribute (when we move to nonnull_if_nonzero for various functions following C2y, this function should be included in that change). The function is declared both for __USE_MISC and for __GLIBC_USE (ISOC23) (so by default not just for compilers defaulting to C23 mode). Tested for x86_64 and x86.
Diffstat (limited to 'string')
-rw-r--r--string/Makefile6
-rw-r--r--string/Versions3
-rw-r--r--string/bits/string_fortified.h12
-rw-r--r--string/memset_explicit.c39
-rw-r--r--string/string.h7
-rw-r--r--string/test-memset.c12
-rw-r--r--string/test-memset_explicit.c19
-rw-r--r--string/tst-xbzero-opt.c4
-rw-r--r--string/tst-xmemset-opt.c2
9 files changed, 102 insertions, 2 deletions
diff --git a/string/Makefile b/string/Makefile
index c83b195e2e..d842ae0457 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -69,6 +69,7 @@ routines := \
mempcpy \
memrchr \
memset \
+ memset_explicit \
rawmemchr \
sigabbrev_np \
sigdescr_np \
@@ -125,6 +126,7 @@ routines_no_fortify += \
memmove \
mempcpy \
memset \
+ memset_explicit \
stpcpy \
stpncpy \
strcat \
@@ -164,6 +166,7 @@ tests := \
test-mempcpy \
test-memrchr \
test-memset \
+ test-memset_explicit \
test-rawmemchr \
test-sig_np \
test-stpcpy \
@@ -210,6 +213,7 @@ tests := \
tst-svc \
tst-svc2 \
tst-xbzero-opt \
+ tst-xmemset-opt \
# tests
tests-static-internal := \
@@ -262,6 +266,7 @@ CFLAGS-stratcliff.c += -fno-builtin
CFLAGS-test-ffs.c += -fno-builtin
CFLAGS-tst-inlcall.c += -fno-builtin
CFLAGS-tst-xbzero-opt.c += -O3
+CFLAGS-tst-xmemset-opt.c += -O3
CFLAGS-test-endian-sign-conversion.c += -Werror -Wsign-conversion
# BZ 21006: Resolve all functions but at least explicit_bzero at startup.
# Otherwise the test fails on s390x as the memcpy in prepare_test_buffer is
@@ -271,6 +276,7 @@ CFLAGS-test-endian-sign-conversion.c += -Werror -Wsign-conversion
# and the call to memmem in count_test_patterns will find a hit of the
# test_pattern on the stack.
LDFLAGS-tst-xbzero-opt = -z now
+LDFLAGS-tst-xmemset-opt = -z now
# Called during TLS initialization.
CFLAGS-memcpy.c += $(no-stack-protector)
diff --git a/string/Versions b/string/Versions
index c56e372a3c..5a63f5ced3 100644
--- a/string/Versions
+++ b/string/Versions
@@ -96,4 +96,7 @@ libc {
strlcat;
strlcpy;
}
+ GLIBC_2.43 {
+ memset_explicit;
+ }
}
diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
index ae14cfbab0..9916e17aa0 100644
--- a/string/bits/string_fortified.h
+++ b/string/bits/string_fortified.h
@@ -60,6 +60,18 @@ __NTH (memset (void *__dest, int __ch, size_t __len))
__glibc_objsize0 (__dest));
}
+#if defined __USE_MISC || __GLIBC_USE (ISOC23)
+void *__memset_explicit_chk (void *__s, int __c, size_t __n, size_t __destlen)
+ __THROW __nonnull ((1)) __fortified_attr_access (__write_only__, 1, 3);
+
+__fortify_function void *
+__NTH (memset_explicit (void *__dest, int __ch, size_t __len))
+{
+ return __memset_explicit_chk (__dest, __ch, __len,
+ __glibc_objsize0 (__dest));
+}
+#endif
+
#ifdef __USE_MISC
# include <bits/strings_fortified.h>
diff --git a/string/memset_explicit.c b/string/memset_explicit.c
new file mode 100644
index 0000000000..29fec30bd9
--- /dev/null
+++ b/string/memset_explicit.c
@@ -0,0 +1,39 @@
+/* Erasure of sensitive data, generic implementation.
+ Copyright (C) 2016-2025 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
+ <https://www.gnu.org/licenses/>. */
+
+/* An assembler implementation of memset_explicit can be created as an
+ assembler alias of an optimized memset implementation.
+ Architecture-specific implementations also need to define
+ __memset_explicit_chk. */
+
+#include <string.h>
+
+/* glibc-internal users use __memset_explicit_chk, and memset_explicit
+ redirects to that. */
+#undef memset_explicit
+
+/* Set LEN bytes of S to C. The compiler will not delete a call to
+ this function, even if S is dead after the call. */
+void *
+memset_explicit (void *s, int c, size_t len)
+{
+ memset (s, c, len);
+ /* Compiler barrier. */
+ asm volatile ("" ::: "memory");
+ return s;
+}
diff --git a/string/string.h b/string/string.h
index df4d489556..81f0b7fd21 100644
--- a/string/string.h
+++ b/string/string.h
@@ -60,6 +60,13 @@ extern void *memccpy (void *__restrict __dest, const void *__restrict __src,
/* Set N bytes of S to C. */
extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));
+#if defined __USE_MISC || __GLIBC_USE (ISOC23)
+/* Like memset, but the compiler will not delete a call to this
+ function, even if S is dead after the call. */
+extern void *memset_explicit (void *__s, int __c, size_t __n)
+ __THROW __nonnull ((1)) __fortified_attr_access (__write_only__, 1, 3);
+#endif
+
/* Compare N bytes of S1 and S2. */
extern int memcmp (const void *__s1, const void *__s2, size_t __n)
__THROW __attribute_pure__ __nonnull ((1, 2));
diff --git a/string/test-memset.c b/string/test-memset.c
index 19c613899b..00664004da 100644
--- a/string/test-memset.c
+++ b/string/test-memset.c
@@ -25,7 +25,11 @@
# endif
#else
# ifndef WIDE
-# define TEST_NAME "memset"
+# ifdef TEST_MEMSET_EXPLICIT
+# define TEST_NAME "memset_explicit"
+# else
+# define TEST_NAME "memset"
+# endif
# else
# define TEST_NAME "wmemset"
# endif /* WIDE */
@@ -34,7 +38,11 @@
#include "test-string.h"
#ifndef WIDE
-# define MEMSET memset
+# ifdef TEST_MEMSET_EXPLICIT
+# define MEMSET memset_explicit
+# else
+# define MEMSET memset
+# endif
# define CHAR char
# define UCHAR unsigned char
# define SIMPLE_MEMSET simple_memset
diff --git a/string/test-memset_explicit.c b/string/test-memset_explicit.c
new file mode 100644
index 0000000000..b02954c6b0
--- /dev/null
+++ b/string/test-memset_explicit.c
@@ -0,0 +1,19 @@
+/* Test and measure memset_explicit.
+ Copyright (C) 2025 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
+ <https://www.gnu.org/licenses/>. */
+#define TEST_MEMSET_EXPLICIT
+#include "test-memset.c"
diff --git a/string/tst-xbzero-opt.c b/string/tst-xbzero-opt.c
index 7f5fbc7ffc..b6e07055d3 100644
--- a/string/tst-xbzero-opt.c
+++ b/string/tst-xbzero-opt.c
@@ -153,7 +153,11 @@ setup_explicit_clear (void)
{
unsigned char buf[TEST_BUFFER_SIZE];
prepare_test_buffer (buf);
+#ifdef TEST_MEMSET_EXPLICIT
+ memset_explicit (buf, 0, TEST_BUFFER_SIZE);
+#else
explicit_bzero (buf, TEST_BUFFER_SIZE);
+#endif
}
enum test_expectation
diff --git a/string/tst-xmemset-opt.c b/string/tst-xmemset-opt.c
new file mode 100644
index 0000000000..ce71a6cea1
--- /dev/null
+++ b/string/tst-xmemset-opt.c
@@ -0,0 +1,2 @@
+#define TEST_MEMSET_EXPLICIT 1
+#include "tst-xbzero-opt.c"