* [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() @ 2026-09-16 21:58 Nathan Chancellor 2026-09-16 22:08 ` Nick Desaulniers 2026-09-17 9:26 ` Jason A. Donenfeld 0 siblings, 2 replies; 11+ messages in thread From: Nathan Chancellor @ 2026-09-16 21:58 UTC (permalink / raw) To: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Jason A. Donenfeld Cc: Vincenzo Frascino, Nick Desaulniers, Bill Wendling, Justin Stitt, linux-kernel, llvm, Nathan Chancellor After a recent change in LLVM [1], RISC-V builds fail when checking the vDSO: arch/riscv/kernel/vdso/vdso.so.dbg: dynamic relocations are not supported make[4]: *** [arch/riscv/kernel/vdso/Makefile:78: arch/riscv/kernel/vdso/vdso.so.dbg] Error 1 memset() is now generated when zeroing params->reserved because LLVM has an optimization (now run in more instances) that can recognize at compile time when it is assigning a static value to a contiguous area of memory and turn that into a call to memset(). Both clang and GCC assume memset() is always available [2]. Hide the value of the iterator variable from the optimizer using OPTIMIZER_HIDE_VAR to inhibit this optimization since it can no longer assume that the zeroing is contiguous. Link: https://github.com/llvm/llvm-project/commit/90cebef1411617fc3eedd359bdf00cb44b1c2439 [1] Link: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/gcc/Standards.html#index-ffreestanding [2] Signed-off-by: Nathan Chancellor <nathan@kernel.org> --- lib/vdso/getrandom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c index 2851afa9154f..d48d1fdb3351 100644 --- a/lib/vdso/getrandom.c +++ b/lib/vdso/getrandom.c @@ -4,6 +4,7 @@ */ #include <linux/array_size.h> +#include <linux/compiler.h> #include <linux/minmax.h> #include <vdso/datapage.h> #include <vdso/getrandom.h> @@ -83,8 +84,11 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ params->size_of_opaque_state = sizeof(*state); params->mmap_prot = PROT_READ | PROT_WRITE; params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; - for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) + for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) { + /* prevent compiler from turning loop into memset() */ + OPTIMIZER_HIDE_VAR(i); params->reserved[i] = 0; + } return 0; } --- base-commit: cee9395acd8043be0644b25c34bfa86623f2b935 change-id: 20260916-vdso-getrandom-avoid-memset-llvm-24-74d45fa6031e Best regards, -- Cheers, Nathan ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-16 21:58 [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() Nathan Chancellor @ 2026-09-16 22:08 ` Nick Desaulniers 2026-09-16 22:57 ` Nathan Chancellor 2026-09-17 9:26 ` Jason A. Donenfeld 1 sibling, 1 reply; 11+ messages in thread From: Nick Desaulniers @ 2026-09-16 22:08 UTC (permalink / raw) To: Nathan Chancellor Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Jason A. Donenfeld, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm On Wed, Sep 16, 2026 at 2:59 PM Nathan Chancellor <nathan@kernel.org> wrote: > > After a recent change in LLVM [1], RISC-V builds fail when checking the > vDSO: > > arch/riscv/kernel/vdso/vdso.so.dbg: dynamic relocations are not supported > make[4]: *** [arch/riscv/kernel/vdso/Makefile:78: arch/riscv/kernel/vdso/vdso.so.dbg] Error 1 > > memset() is now generated when zeroing params->reserved because LLVM has > an optimization (now run in more instances) that can recognize at > compile time when it is assigning a static value to a contiguous area of > memory and turn that into a call to memset(). Both clang and GCC assume > memset() is always available [2]. > > Hide the value of the iterator variable from the optimizer using > OPTIMIZER_HIDE_VAR to inhibit this optimization since it can no longer > assume that the zeroing is contiguous. > > Link: https://github.com/llvm/llvm-project/commit/90cebef1411617fc3eedd359bdf00cb44b1c2439 [1] > Link: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/gcc/Standards.html#index-ffreestanding [2] > Signed-off-by: Nathan Chancellor <nathan@kernel.org> Link: https://github.com/ClangBuiltLinux/linux/issues/2183 Do we need to tag stable (I imagine this might be visible for older kernels with newer llvm)? Any idea why we only see this for riscv? Would think loop idiom recognition would make this transform for any target. Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> > --- > lib/vdso/getrandom.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c > index 2851afa9154f..d48d1fdb3351 100644 > --- a/lib/vdso/getrandom.c > +++ b/lib/vdso/getrandom.c > @@ -4,6 +4,7 @@ > */ > > #include <linux/array_size.h> > +#include <linux/compiler.h> > #include <linux/minmax.h> > #include <vdso/datapage.h> > #include <vdso/getrandom.h> > @@ -83,8 +84,11 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ > params->size_of_opaque_state = sizeof(*state); > params->mmap_prot = PROT_READ | PROT_WRITE; > params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; > - for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) > + for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) { > + /* prevent compiler from turning loop into memset() */ > + OPTIMIZER_HIDE_VAR(i); > params->reserved[i] = 0; > + } > return 0; > } > > > --- > base-commit: cee9395acd8043be0644b25c34bfa86623f2b935 > change-id: 20260916-vdso-getrandom-avoid-memset-llvm-24-74d45fa6031e > > Best regards, > -- > Cheers, > Nathan > -- Thanks, ~Nick Desaulniers ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-16 22:08 ` Nick Desaulniers @ 2026-09-16 22:57 ` Nathan Chancellor 2026-09-16 23:40 ` Nick Desaulniers 0 siblings, 1 reply; 11+ messages in thread From: Nathan Chancellor @ 2026-09-16 22:57 UTC (permalink / raw) To: Nick Desaulniers Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Jason A. Donenfeld, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm On Wed, Sep 16, 2026 at 03:08:38PM -0700, Nick Desaulniers wrote: > Link: https://github.com/ClangBuiltLinux/linux/issues/2183 Whoops :/ thanks for including this > Do we need to tag stable (I imagine this might be visible for older > kernels with newer llvm)? Yeah, we probably do, as I expect this error to happen with 6.16+ due to commit ee0d03053e70 ("RISC-V: vDSO: Wire up getrandom() vDSO implementation"). I will include those in v2 once the maintainers have had some time to chime in to make sure this approach is okay. > Any idea why we only see this for riscv? Would think loop idiom > recognition would make this transform for any target. I have not confirmed this but based on looking at the aarch64 disassembly when deciding how to tackle this, I suspect some backends can expand certain calls to memset() to a set of instructions, as I saw a set of stp calls with xzr as the source and offsets into params as the dest. > Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> Thanks for taking a look! -- Cheers, Nathan ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-16 22:57 ` Nathan Chancellor @ 2026-09-16 23:40 ` Nick Desaulniers 0 siblings, 0 replies; 11+ messages in thread From: Nick Desaulniers @ 2026-09-16 23:40 UTC (permalink / raw) To: Nathan Chancellor Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Jason A. Donenfeld, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm On Wed, Sep 16, 2026 at 3:57 PM Nathan Chancellor <nathan@kernel.org> wrote: > > > Any idea why we only see this for riscv? Would think loop idiom > > recognition would make this transform for any target. > > I have not confirmed this but based on looking at the aarch64 > disassembly when deciding how to tackle this, I suspect some backends > can expand certain calls to memset() to a set of instructions, as I saw > a set of stp calls with xzr as the source and offsets into params as the > dest. https://godbolt.org/z/dbPrexfvo Yeah, just seems like there's a heuristic/threshold for the trip count. (GCC will also generate the libcall for trip counts >= 16). -- Thanks, ~Nick Desaulniers ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-16 21:58 [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() Nathan Chancellor 2026-09-16 22:08 ` Nick Desaulniers @ 2026-09-17 9:26 ` Jason A. Donenfeld 2026-09-17 17:39 ` Nathan Chancellor 1 sibling, 1 reply; 11+ messages in thread From: Jason A. Donenfeld @ 2026-09-17 9:26 UTC (permalink / raw) To: Nathan Chancellor Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Nick Desaulniers, Bill Wendling, Justin Stitt, linux-kernel, llvm On Wed, Sep 16, 2026 at 02:58:44PM -0700, Nathan Chancellor wrote: > After a recent change in LLVM [1], RISC-V builds fail when checking the > vDSO: > > arch/riscv/kernel/vdso/vdso.so.dbg: dynamic relocations are not supported > make[4]: *** [arch/riscv/kernel/vdso/Makefile:78: arch/riscv/kernel/vdso/vdso.so.dbg] Error 1 > > memset() is now generated when zeroing params->reserved because LLVM has > an optimization (now run in more instances) that can recognize at > compile time when it is assigning a static value to a contiguous area of > memory and turn that into a call to memset(). Both clang and GCC assume > memset() is always available [2]. > > Hide the value of the iterator variable from the optimizer using > OPTIMIZER_HIDE_VAR to inhibit this optimization since it can no longer > assume that the zeroing is contiguous. > > Link: https://github.com/llvm/llvm-project/commit/90cebef1411617fc3eedd359bdf00cb44b1c2439 [1] > Link: https://gcc.gnu.org/onlinedocs/gcc-16.2.0/gcc/Standards.html#index-ffreestanding [2] > Signed-off-by: Nathan Chancellor <nathan@kernel.org> > --- > lib/vdso/getrandom.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c > index 2851afa9154f..d48d1fdb3351 100644 > --- a/lib/vdso/getrandom.c > +++ b/lib/vdso/getrandom.c > @@ -4,6 +4,7 @@ > */ > > #include <linux/array_size.h> > +#include <linux/compiler.h> > #include <linux/minmax.h> > #include <vdso/datapage.h> > #include <vdso/getrandom.h> > @@ -83,8 +84,11 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ > params->size_of_opaque_state = sizeof(*state); > params->mmap_prot = PROT_READ | PROT_WRITE; > params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; > - for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) > + for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) { > + /* prevent compiler from turning loop into memset() */ > + OPTIMIZER_HIDE_VAR(i); > params->reserved[i] = 0; > + } > return 0; > } I don't suspect this is the right change. On x86_64, this changes to code from: rep stosq into: loc_2D9: mov dword ptr [rbx+rax*4+0Ch], 0 add rax, 1 cmp rax, 0Ch jbe short loc_2D9 Which is a lot less compact. It seems like the actual solution is for gcc&clang to emit this inline memset mnemonic when the platform has a good one, and otherwise not. But disabling optimizations for all platforms, because it's broken on one, seems bad. In this case, the compiler is being smart: it identifies a loop and rightly turns it into memset. But if this isn't a compilation environment that has an outline function, it should do something else. Jason ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-17 9:26 ` Jason A. Donenfeld @ 2026-09-17 17:39 ` Nathan Chancellor 2026-09-17 22:49 ` Nick Desaulniers 0 siblings, 1 reply; 11+ messages in thread From: Nathan Chancellor @ 2026-09-17 17:39 UTC (permalink / raw) To: Jason A. Donenfeld Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Nick Desaulniers, Bill Wendling, Justin Stitt, linux-kernel, llvm On Thu, Sep 17, 2026 at 11:26:46AM +0200, Jason A. Donenfeld wrote: > I don't suspect this is the right change. On x86_64, this changes to > code from: > > rep stosq > > into: > > loc_2D9: > mov dword ptr [rbx+rax*4+0Ch], 0 > add rax, 1 > cmp rax, 0Ch > jbe short loc_2D9 > > Which is a lot less compact. It seems like the actual solution is for > gcc&clang to emit this inline memset mnemonic when the platform has a > good one, and otherwise not. But disabling optimizations for all > platforms, because it's broken on one, seems bad. Yes, that is certainly fair criticism. That said, this optimization happens in the middle end as far as I can tell, so I am not sure how much target specific knowledge is available at that point. Additionally... > In this case, the compiler is being smart: it identifies a loop and > rightly turns it into memset. But if this isn't a compilation > environment that has an outline function, it should do something else. As I mentioned in the commit message, compilers require all environments (hosted or not) to provide memset(), so the "doing something else" is nothing :) GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp. I guess another option is to just include a basic memset() like the one in lib/string.c so that it is only used if the compiler makes this sort of transformation, while leaving all other architectures alone. -- Cheers, Nathan ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-17 17:39 ` Nathan Chancellor @ 2026-09-17 22:49 ` Nick Desaulniers 2026-09-18 8:20 ` Jason A. Donenfeld 0 siblings, 1 reply; 11+ messages in thread From: Nick Desaulniers @ 2026-09-17 22:49 UTC (permalink / raw) To: Jason A. Donenfeld Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm, Nathan Chancellor On Thu, Sep 17, 2026 at 10:39 AM Nathan Chancellor <nathan@kernel.org> wrote: > > On Thu, Sep 17, 2026 at 11:26:46AM +0200, Jason A. Donenfeld wrote: > > I don't suspect this is the right change. On x86_64, this changes to > > code from: > > > > rep stosq > > > > into: > > > > loc_2D9: > > mov dword ptr [rbx+rax*4+0Ch], 0 > > add rax, 1 > > cmp rax, 0Ch > > jbe short loc_2D9 /me nods > > > > Which is a lot less compact. It seems like the actual solution is for > > gcc&clang to emit this inline memset mnemonic when the platform has a > > good one, and otherwise not. But disabling optimizations for all > > platforms, because it's broken on one, seems bad. Ideally, yeah. Pragmatically: the compiler doesn't know what you will link against or not; so it just emits relocations that the linker will (hopefully) resolve. I've definitely looked at how llvm decides when to emit libcalls to compiler-rt/libgcc ("the compiler runtime") and thought "I wonder how this works with compiler runtime version N-1?" There's also the requirement gcc and clang have about memcpy, memmove, memset, memcmp always being available (-ffreestanding or not) noted below. > > In this case, the compiler is being smart: it identifies a loop and > > rightly turns it into memset. But if this isn't a compilation > > environment that has an outline function, it should do something else. > > As I mentioned in the commit message, compilers require all environments > (hosted or not) to provide memset(), so the "doing something else" is > nothing :) > > GCC requires the freestanding environment provide memcpy, memmove, > memset and memcmp. +1 > > I guess another option is to just include a basic memset() like the one > in lib/string.c so that it is only used if the compiler makes this sort > of transformation, while leaving all other architectures alone. It's also possible to get GCC to emit the libcall, even in the presence of -ffreestanding: https://godbolt.org/z/6hTrYq9z8 And it's not the first time this code in particular has had this issue; https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7bad082e113640fc81200ff869e5c2d7a9c29a2 (is this patch a Fixes: for that?) Clang has __builtin_memset_inline to avoid explicit libcalls; AFAICT GCC does not. So we could use that here to provide such a guarantee with clang; the code would remain brittle and likely break again with GCC. > > -- > Cheers, > Nathan -- Thanks, ~Nick Desaulniers ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-17 22:49 ` Nick Desaulniers @ 2026-09-18 8:20 ` Jason A. Donenfeld 2026-09-18 12:59 ` Jason A. Donenfeld 0 siblings, 1 reply; 11+ messages in thread From: Jason A. Donenfeld @ 2026-09-18 8:20 UTC (permalink / raw) To: Nick Desaulniers Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm, Nathan Chancellor On Thu, Sep 17, 2026 at 6:49 PM Nick Desaulniers <ndesaulniers@google.com> wrote: > > On Thu, Sep 17, 2026 at 10:39 AM Nathan Chancellor <nathan@kernel.org> wrote: > > > > On Thu, Sep 17, 2026 at 11:26:46AM +0200, Jason A. Donenfeld wrote: > > > I don't suspect this is the right change. On x86_64, this changes to > > > code from: > > > > > > rep stosq > > > > > > into: > > > > > > loc_2D9: > > > mov dword ptr [rbx+rax*4+0Ch], 0 > > > add rax, 1 > > > cmp rax, 0Ch > > > jbe short loc_2D9 > > /me nods > > > > > > > Which is a lot less compact. It seems like the actual solution is for > > > gcc&clang to emit this inline memset mnemonic when the platform has a > > > good one, and otherwise not. But disabling optimizations for all > > > platforms, because it's broken on one, seems bad. > > Ideally, yeah. > Pragmatically: > the compiler doesn't know what you will link against or not; so it > just emits relocations that the linker will (hopefully) resolve. I've > definitely looked at how llvm decides when to emit libcalls to > compiler-rt/libgcc ("the compiler runtime") and thought "I wonder how > this works with compiler runtime version N-1?" > > There's also the requirement gcc and clang have about memcpy, memmove, > memset, memcmp always being available (-ffreestanding or not) noted > below. > > > > In this case, the compiler is being smart: it identifies a loop and > > > rightly turns it into memset. But if this isn't a compilation > > > environment that has an outline function, it should do something else. > > > > As I mentioned in the commit message, compilers require all environments > > (hosted or not) to provide memset(), so the "doing something else" is > > nothing :) > > > > GCC requires the freestanding environment provide memcpy, memmove, > > memset and memcmp. > > +1 > > > > > I guess another option is to just include a basic memset() like the one > > in lib/string.c so that it is only used if the compiler makes this sort > > of transformation, while leaving all other architectures alone. > > It's also possible to get GCC to emit the libcall, even in the > presence of -ffreestanding: https://godbolt.org/z/6hTrYq9z8 > > And it's not the first time this code in particular has had this issue; > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7bad082e113640fc81200ff869e5c2d7a9c29a2 > > (is this patch a Fixes: for that?) > > Clang has __builtin_memset_inline to avoid explicit libcalls; AFAICT > GCC does not. So we could use that here to provide such a guarantee > with clang; the code would remain brittle and likely break again with > GCC. It sounds like this is broken currently only on clang on riscv, right? So maybe we can use __builtin_memset_inline, and get something similar into gcc, before a future gcc version also breaks? That way it doesn't break in the future. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-18 8:20 ` Jason A. Donenfeld @ 2026-09-18 12:59 ` Jason A. Donenfeld 2026-09-18 19:19 ` Jason A. Donenfeld 0 siblings, 1 reply; 11+ messages in thread From: Jason A. Donenfeld @ 2026-09-18 12:59 UTC (permalink / raw) To: Nick Desaulniers Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm, Nathan Chancellor On Fri, Sep 18, 2026 at 10:20:45AM +0200, Jason A. Donenfeld wrote: > On Thu, Sep 17, 2026 at 6:49 PM Nick Desaulniers > <ndesaulniers@google.com> wrote: > > > > On Thu, Sep 17, 2026 at 10:39 AM Nathan Chancellor <nathan@kernel.org> wrote: > > > > > > On Thu, Sep 17, 2026 at 11:26:46AM +0200, Jason A. Donenfeld wrote: > > > > I don't suspect this is the right change. On x86_64, this changes to > > > > code from: > > > > > > > > rep stosq > > > > > > > > into: > > > > > > > > loc_2D9: > > > > mov dword ptr [rbx+rax*4+0Ch], 0 > > > > add rax, 1 > > > > cmp rax, 0Ch > > > > jbe short loc_2D9 > > > > /me nods > > > > > > > > > > Which is a lot less compact. It seems like the actual solution is for > > > > gcc&clang to emit this inline memset mnemonic when the platform has a > > > > good one, and otherwise not. But disabling optimizations for all > > > > platforms, because it's broken on one, seems bad. > > > > Ideally, yeah. > > Pragmatically: > > the compiler doesn't know what you will link against or not; so it > > just emits relocations that the linker will (hopefully) resolve. I've > > definitely looked at how llvm decides when to emit libcalls to > > compiler-rt/libgcc ("the compiler runtime") and thought "I wonder how > > this works with compiler runtime version N-1?" > > > > There's also the requirement gcc and clang have about memcpy, memmove, > > memset, memcmp always being available (-ffreestanding or not) noted > > below. > > > > > > In this case, the compiler is being smart: it identifies a loop and > > > > rightly turns it into memset. But if this isn't a compilation > > > > environment that has an outline function, it should do something else. > > > > > > As I mentioned in the commit message, compilers require all environments > > > (hosted or not) to provide memset(), so the "doing something else" is > > > nothing :) > > > > > > GCC requires the freestanding environment provide memcpy, memmove, > > > memset and memcmp. > > > > +1 > > > > > > > > I guess another option is to just include a basic memset() like the one > > > in lib/string.c so that it is only used if the compiler makes this sort > > > of transformation, while leaving all other architectures alone. > > > > It's also possible to get GCC to emit the libcall, even in the > > presence of -ffreestanding: https://godbolt.org/z/6hTrYq9z8 > > > > And it's not the first time this code in particular has had this issue; > > > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7bad082e113640fc81200ff869e5c2d7a9c29a2 > > > > (is this patch a Fixes: for that?) > > > > Clang has __builtin_memset_inline to avoid explicit libcalls; AFAICT > > GCC does not. So we could use that here to provide such a guarantee > > with clang; the code would remain brittle and likely break again with > > GCC. > > It sounds like this is broken currently only on clang on riscv, right? > So maybe we can use __builtin_memset_inline, and get something similar > into gcc, before a future gcc version also breaks? That way it doesn't > break in the future. There's -finline-stringops=memset for gcc (>=14), which should keep things sane there. So, we can use that on gcc, and alias memset to __builtin_memset_inline on clang. And then we should be good? ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-18 12:59 ` Jason A. Donenfeld @ 2026-09-18 19:19 ` Jason A. Donenfeld 2026-09-18 21:07 ` Nathan Chancellor 0 siblings, 1 reply; 11+ messages in thread From: Jason A. Donenfeld @ 2026-09-18 19:19 UTC (permalink / raw) To: Nick Desaulniers Cc: Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm, Nathan Chancellor On Fri, Sep 18, 2026 at 02:59:37PM +0200, Jason A. Donenfeld wrote: > On Fri, Sep 18, 2026 at 10:20:45AM +0200, Jason A. Donenfeld wrote: > > On Thu, Sep 17, 2026 at 6:49 PM Nick Desaulniers > > <ndesaulniers@google.com> wrote: > > > > > > On Thu, Sep 17, 2026 at 10:39 AM Nathan Chancellor <nathan@kernel.org> wrote: > > > > > > > > On Thu, Sep 17, 2026 at 11:26:46AM +0200, Jason A. Donenfeld wrote: > > > > > I don't suspect this is the right change. On x86_64, this changes to > > > > > code from: > > > > > > > > > > rep stosq > > > > > > > > > > into: > > > > > > > > > > loc_2D9: > > > > > mov dword ptr [rbx+rax*4+0Ch], 0 > > > > > add rax, 1 > > > > > cmp rax, 0Ch > > > > > jbe short loc_2D9 > > > > > > /me nods > > > > > > > > > > > > > Which is a lot less compact. It seems like the actual solution is for > > > > > gcc&clang to emit this inline memset mnemonic when the platform has a > > > > > good one, and otherwise not. But disabling optimizations for all > > > > > platforms, because it's broken on one, seems bad. > > > > > > Ideally, yeah. > > > Pragmatically: > > > the compiler doesn't know what you will link against or not; so it > > > just emits relocations that the linker will (hopefully) resolve. I've > > > definitely looked at how llvm decides when to emit libcalls to > > > compiler-rt/libgcc ("the compiler runtime") and thought "I wonder how > > > this works with compiler runtime version N-1?" > > > > > > There's also the requirement gcc and clang have about memcpy, memmove, > > > memset, memcmp always being available (-ffreestanding or not) noted > > > below. > > > > > > > > In this case, the compiler is being smart: it identifies a loop and > > > > > rightly turns it into memset. But if this isn't a compilation > > > > > environment that has an outline function, it should do something else. > > > > > > > > As I mentioned in the commit message, compilers require all environments > > > > (hosted or not) to provide memset(), so the "doing something else" is > > > > nothing :) > > > > > > > > GCC requires the freestanding environment provide memcpy, memmove, > > > > memset and memcmp. > > > > > > +1 > > > > > > > > > > > I guess another option is to just include a basic memset() like the one > > > > in lib/string.c so that it is only used if the compiler makes this sort > > > > of transformation, while leaving all other architectures alone. > > > > > > It's also possible to get GCC to emit the libcall, even in the > > > presence of -ffreestanding: https://godbolt.org/z/6hTrYq9z8 > > > > > > And it's not the first time this code in particular has had this issue; > > > > > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7bad082e113640fc81200ff869e5c2d7a9c29a2 > > > > > > (is this patch a Fixes: for that?) > > > > > > Clang has __builtin_memset_inline to avoid explicit libcalls; AFAICT > > > GCC does not. So we could use that here to provide such a guarantee > > > with clang; the code would remain brittle and likely break again with > > > GCC. > > > > It sounds like this is broken currently only on clang on riscv, right? > > So maybe we can use __builtin_memset_inline, and get something similar > > into gcc, before a future gcc version also breaks? That way it doesn't > > break in the future. > > There's -finline-stringops=memset for gcc (>=14), which should keep > things sane there. So, we can use that on gcc, and alias memset to > __builtin_memset_inline on clang. And then we should be good? Untested, but putting this all together I suppose looks like this, if you want to play around and see: diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index 7dec05dd33b7..3b98058f95ea 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -41,7 +41,7 @@ CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ -Wmissing-prototypes -Wmissing-declarations -CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables +CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables $(call cc-option,-finline-stringops=memset) CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_REMOVE_VDSO) CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO) diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 9c9181bb4071..4c6b597a7665 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -16,6 +16,7 @@ ccflags-vdso := \ $(filter -m64,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) \ $(filter -m%-float,$(KBUILD_CFLAGS)) \ + $(call cc-option,-finline-stringops=memset) \ $(CLANG_FLAGS) \ -D__VDSO__ diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 8dbf2532a573..720f95cd24f8 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -36,6 +36,7 @@ endif ccflags-y := -fno-stack-protector ccflags-y += -DDISABLE_BRANCH_PROFILING ccflags-y += -fno-builtin +ccflags-y += $(call cc-option,-finline-stringops=memset) ccflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) ccflags-y += $(CFI_FULL) asflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile index 35c834b895ec..9d0ae1f38388 100644 --- a/arch/s390/kernel/vdso/Makefile +++ b/arch/s390/kernel/vdso/Makefile @@ -29,6 +29,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE) +KBUILD_CFLAGS_VDSO += $(call cc-option,-finline-stringops=memset) ldflags-y := -shared -soname=linux-vdso.so.1 \ --hash-style=both --build-id=sha1 \ $(call ld-option, --eh-frame-hdr) -T diff --git a/arch/x86/entry/vdso/vdso64/Makefile b/arch/x86/entry/vdso/vdso64/Makefile index 7c0790065b5e..751cb6f99b65 100644 --- a/arch/x86/entry/vdso/vdso64/Makefile +++ b/arch/x86/entry/vdso/vdso64/Makefile @@ -14,7 +14,7 @@ vobjs-$(CONFIG_X86_SGX) += vsgx.o vobjs-$(CONFIG_FUTEX_ROBUST_UNLOCK) += vfutex.o # Compilation flags -flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small +flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small $(call cc-option,-finline-stringops=memset) # The location of this include matters! include $(src)/../common/Makefile.include diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c index 2851afa9154f..713e95dfdedc 100644 --- a/lib/vdso/getrandom.c +++ b/lib/vdso/getrandom.c @@ -29,6 +29,12 @@ } \ } while (0) +#if __has_builtin(__builtin_memset_inline) +#define memset(dst, value, size) __builtin_memset_inline(dst, value, size) +#else +#define memset(dst, value, size) __builtin_memset(dst, value, size) +#endif + static void memcpy_and_zero_src(void *dst, void *src, size_t len) { if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) { @@ -83,8 +89,7 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ params->size_of_opaque_state = sizeof(*state); params->mmap_prot = PROT_READ | PROT_WRITE; params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; - for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) - params->reserved[i] = 0; + memset(params->reserved, 0, sizeof(params->reserved)); return 0; } ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() 2026-09-18 19:19 ` Jason A. Donenfeld @ 2026-09-18 21:07 ` Nathan Chancellor 0 siblings, 0 replies; 11+ messages in thread From: Nathan Chancellor @ 2026-09-18 21:07 UTC (permalink / raw) To: Jason A. Donenfeld Cc: Nick Desaulniers, Andy Lutomirski, Thomas Gleixner, Theodore Ts'o, Vincenzo Frascino, Bill Wendling, Justin Stitt, linux-kernel, llvm On Fri, Sep 18, 2026 at 09:19:00PM +0200, Jason A. Donenfeld wrote: > Untested, but putting this all together I suppose looks like this, if > you want to play around and see: Thanks! > diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c > index 2851afa9154f..713e95dfdedc 100644 > --- a/lib/vdso/getrandom.c > +++ b/lib/vdso/getrandom.c > @@ -29,6 +29,12 @@ > } \ > } while (0) > > +#if __has_builtin(__builtin_memset_inline) > +#define memset(dst, value, size) __builtin_memset_inline(dst, value, size) > +#else > +#define memset(dst, value, size) __builtin_memset(dst, value, size) > +#endif > + > static void memcpy_and_zero_src(void *dst, void *src, size_t len) > { > if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) { > @@ -83,8 +89,7 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ > params->size_of_opaque_state = sizeof(*state); > params->mmap_prot = PROT_READ | PROT_WRITE; > params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; > - for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) > - params->reserved[i] = 0; > + memset(params->reserved, 0, sizeof(params->reserved)); > return 0; > } This will regress GCC < 14 because '-finline-stringops=memset' does not exist yet, meaning __builtin_memset() may still generated a call to memset(). Testing GCC 13 with ARCH=riscv defconfig, I get the same error as originally reported up thread. I am not sure we can get rid of the loop for that reason. I tested the following diff that keeps clang (__builtin_memset_inline) GCC 14+ (-finline-stringops=memset + __builtin_memset) GCC <14 (simple loop) happy (and moves the checks into Kconfig, which is being discussed at a wider level [1]) but it is admittedly slightly uglier... That said, it certainly seems more robust. [1]: https://lore.kernel.org/linux-kbuild/20260917-build-speedup-v3-11-9ecf4163ff36@kernel.org diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index 7dec05dd33b7..f6619e1cb2ce 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -41,7 +41,7 @@ CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ -Wmissing-prototypes -Wmissing-declarations -CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables +CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables $(CONFIG_CC_OPT_INLINE_MEMSET) CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_REMOVE_VDSO) CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO) diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 9c9181bb4071..0b84892d084b 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -16,6 +16,7 @@ ccflags-vdso := \ $(filter -m64,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) \ $(filter -m%-float,$(KBUILD_CFLAGS)) \ + $(CONFIG_CC_OPT_INLINE_MEMSET) \ $(CLANG_FLAGS) \ -D__VDSO__ diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 8dbf2532a573..c023046a3fd7 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -36,6 +36,7 @@ endif ccflags-y := -fno-stack-protector ccflags-y += -DDISABLE_BRANCH_PROFILING ccflags-y += -fno-builtin +ccflags-y += $(CONFIG_CC_OPT_INLINE_MEMSET) ccflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) ccflags-y += $(CFI_FULL) asflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile index 35c834b895ec..54bcf2984ca1 100644 --- a/arch/s390/kernel/vdso/Makefile +++ b/arch/s390/kernel/vdso/Makefile @@ -29,6 +29,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE) +KBUILD_CFLAGS_VDSO += $(CONFIG_CC_OPT_INLINE_MEMSET) ldflags-y := -shared -soname=linux-vdso.so.1 \ --hash-style=both --build-id=sha1 \ $(call ld-option, --eh-frame-hdr) -T diff --git a/arch/x86/entry/vdso/vdso64/Makefile b/arch/x86/entry/vdso/vdso64/Makefile index 7c0790065b5e..c2353a065279 100644 --- a/arch/x86/entry/vdso/vdso64/Makefile +++ b/arch/x86/entry/vdso/vdso64/Makefile @@ -14,7 +14,7 @@ vobjs-$(CONFIG_X86_SGX) += vsgx.o vobjs-$(CONFIG_FUTEX_ROBUST_UNLOCK) += vfutex.o # Compilation flags -flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small +flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small $(CONFIG_CC_OPT_INLINE_MEMSET) # The location of this include matters! include $(src)/../common/Makefile.include diff --git a/init/Kconfig b/init/Kconfig index 6e82b4957a5d..09251b5f79d4 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -173,6 +173,13 @@ config CC_HAS_ALLOC_TOKEN config CC_HAS_MULTIDIMENSIONAL_NONSTRING def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror) +config CC_HAS_OPT_INLINE_MEMSET + def_bool $(cc-option,-finline-stringops=memset) + +config CC_OPT_INLINE_MEMSET + string + default "-finline-stringops=memset" if CC_HAS_OPT_INLINE_MEMSET + config LD_CAN_USE_KEEP_IN_OVERLAY # ld.lld prior to 21.0.0 did not support KEEP within an overlay description # https://github.com/llvm/llvm-project/pull/130661 diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c index 2851afa9154f..83831dab08b2 100644 --- a/lib/vdso/getrandom.c +++ b/lib/vdso/getrandom.c @@ -29,6 +29,12 @@ } \ } while (0) +#if __has_builtin(__builtin_memset_inline) +#define memset_inline(dst, value, size) __builtin_memset_inline(dst, value, size) +#elif IS_ENABLED(CONFIG_CC_HAS_OPT_INLINE_MEMSET) +#define memset_inline(dst, value, size) __builtin_memset(dst, value, size) +#endif + static void memcpy_and_zero_src(void *dst, void *src, size_t len) { if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) { @@ -83,8 +89,12 @@ __cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_ params->size_of_opaque_state = sizeof(*state); params->mmap_prot = PROT_READ | PROT_WRITE; params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS; +#ifdef memset_inline + memset_inline(params->reserved, 0, sizeof(params->reserved)); +#else for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i) params->reserved[i] = 0; +#endif return 0; } -- Cheers, Nathan ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-18 21:07 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-16 21:58 [PATCH] random: vDSO: Avoid call to memset() when zeroing reserved in __cvdso_getrandom_data() Nathan Chancellor 2026-09-16 22:08 ` Nick Desaulniers 2026-09-16 22:57 ` Nathan Chancellor 2026-09-16 23:40 ` Nick Desaulniers 2026-09-17 9:26 ` Jason A. Donenfeld 2026-09-17 17:39 ` Nathan Chancellor 2026-09-17 22:49 ` Nick Desaulniers 2026-09-18 8:20 ` Jason A. Donenfeld 2026-09-18 12:59 ` Jason A. Donenfeld 2026-09-18 19:19 ` Jason A. Donenfeld 2026-09-18 21:07 ` Nathan Chancellor
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®