Skip to content

Commit edcc8a3

Browse files
Qi Xiarndb
authored andcommitted
once: fix race by moving DO_ONCE to separate section
The commit c2c60ea ("once: use __section(".data.once")") moved DO_ONCE's ___done variable to .data.once section, which conflicts with DO_ONCE_LITE() that also uses the same section. This creates a race condition when clear_warn_once is used: Thread 1 (DO_ONCE) Thread 2 (DO_ONCE) __do_once_start read ___done (false) acquire once_lock execute func __do_once_done write ___done (true) __do_once_start release once_lock // Thread 3 clear_warn_once reset ___done read ___done (false) acquire once_lock execute func schedule once_work __do_once_done once_deferred: OK write ___done (true) static_branch_disable release once_lock schedule once_work once_deferred: BUG_ON(!static_key_enabled) DO_ONCE_LITE() in once_lite.h is used by WARN_ON_ONCE() and other warning macros. Keep its ___done flag in the .data..once section and allow resetting by clear_warn_once, as originally intended. In contrast, DO_ONCE() is used for functions like get_random_once() and relies on its ___done flag for internal synchronization. We should not reset DO_ONCE() by clear_warn_once. Fix it by isolating DO_ONCE's ___done into a separate .data..do_once section, shielding it from clear_warn_once. Fixes: c2c60ea ("once: use __section(".data.once")") Reported-by: Hulk Robot <hulkci@huawei.com> Signed-off-by: Qi Xi <xiqi2@huawei.com> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
1 parent 8327bd4 commit edcc8a3

2 files changed

Lines changed: 3 additions & 2 deletions

File tree

include/asm-generic/vmlinux.lds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
361361
__start_once = .; \
362362
*(.data..once) \
363363
__end_once = .; \
364+
*(.data..do_once) \
364365
STRUCT_ALIGN(); \
365366
*(__tracepoints) \
366367
/* implement dynamic printk debug */ \

include/linux/once.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key,
4646
#define DO_ONCE(func, ...) \
4747
({ \
4848
bool ___ret = false; \
49-
static bool __section(".data..once") ___done = false; \
49+
static bool __section(".data..do_once") ___done = false; \
5050
static DEFINE_STATIC_KEY_TRUE(___once_key); \
5151
if (static_branch_unlikely(&___once_key)) { \
5252
unsigned long ___flags; \
@@ -64,7 +64,7 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key,
6464
#define DO_ONCE_SLEEPABLE(func, ...) \
6565
({ \
6666
bool ___ret = false; \
67-
static bool __section(".data..once") ___done = false; \
67+
static bool __section(".data..do_once") ___done = false; \
6868
static DEFINE_STATIC_KEY_TRUE(___once_key); \
6969
if (static_branch_unlikely(&___once_key)) { \
7070
___ret = __do_once_sleepable_start(&___done); \

0 commit comments

Comments
 (0)