Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

random: vDSO: don't use 64-bit atomics on 32-bit architectures

Performing SMP atomic operations on u64 fails on powerpc32:

CC drivers/char/random.o
In file included from <command-line>:
drivers/char/random.c: In function 'crng_reseed':
././include/linux/compiler_types.h:510:45: error: call to '__compiletime_assert_391' declared with attribute error: Need native word sized stores/loads for atomicity.
510 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^
././include/linux/compiler_types.h:491:25: note: in definition of macro '__compiletime_assert'
491 | prefix ## suffix(); \
| ^~~~~~
././include/linux/compiler_types.h:510:9: note: in expansion of macro '_compiletime_assert'
510 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ^~~~~~~~~~~~~~~~~~~
././include/linux/compiler_types.h:513:9: note: in expansion of macro 'compiletime_assert'
513 | compiletime_assert(__native_word(t), \
| ^~~~~~~~~~~~~~~~~~
./arch/powerpc/include/asm/barrier.h:74:9: note: in expansion of macro 'compiletime_assert_atomic_type'
74 | compiletime_assert_atomic_type(*p); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./include/asm-generic/barrier.h:172:55: note: in expansion of macro '__smp_store_release'
172 | #define smp_store_release(p, v) do { kcsan_release(); __smp_store_release(p, v); } while (0)
| ^~~~~~~~~~~~~~~~~~~
drivers/char/random.c:286:9: note: in expansion of macro 'smp_store_release'
286 | smp_store_release(&__arch_get_k_vdso_rng_data()->generation, next_gen + 1);
| ^~~~~~~~~~~~~~~~~

The kernel-side generation counter in the random driver is handled as an
unsigned long, not as a u64, in base_crng and struct crng.

But on the vDSO side, it needs to be an u64, not just an unsigned long,
in order to support a 32-bit vDSO atop a 64-bit kernel.

On kernel side, however, it is an unsigned long, hence a 32-bit value on
32-bit architectures, so just cast it to unsigned long for the
smp_store_release(). A side effect is that on big endian architectures
the store will be performed in the upper 32 bits. It is not an issue on
its own because the vDSO site doesn't mind the value, as it only checks
differences. Just make sure that the vDSO side checks the full 64 bits.
For that, the local current_generation has to be u64 as well.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>

authored by

Christophe Leroy and committed by
Jason A. Donenfeld
81c68960 7fe5b3e4

+9 -2
+8 -1
drivers/char/random.c
··· 281 281 * former to arrive at the latter. Use smp_store_release so that this 282 282 * is ordered with the write above to base_crng.generation. Pairs with 283 283 * the smp_rmb() before the syscall in the vDSO code. 284 + * 285 + * Cast to unsigned long for 32-bit architectures, since atomic 64-bit 286 + * operations are not supported on those architectures. This is safe 287 + * because base_crng.generation is a 32-bit value. On big-endian 288 + * architectures it will be stored in the upper 32 bits, but that's okay 289 + * because the vDSO side only checks whether the value changed, without 290 + * actually using or interpreting the value. 284 291 */ 285 - smp_store_release(&_vdso_rng_data.generation, next_gen + 1); 292 + smp_store_release((unsigned long *)&_vdso_rng_data.generation, next_gen + 1); 286 293 #endif 287 294 if (!static_branch_likely(&crng_is_ready)) 288 295 crng_init = CRNG_READY;
+1 -1
lib/vdso/getrandom.c
··· 68 68 struct vgetrandom_state *state = opaque_state; 69 69 size_t batch_len, nblocks, orig_len = len; 70 70 bool in_use, have_retried = false; 71 - unsigned long current_generation; 72 71 void *orig_buffer = buffer; 72 + u64 current_generation; 73 73 u32 counter[2] = { 0 }; 74 74 75 75 if (unlikely(opaque_len == ~0UL && !buffer && !len && !flags)) {