From ff153903cb49bbb28fcf5a23837985347c3519fa Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Wed, 3 Aug 2011 10:52:32 +1000 Subject: [PATCH] On thread exit shm_exit_ns() is called, it uses shm_ids(ns).rw_mutex. It is initialized in shm_init(), but it is not called yet at the moment of kernel threads exit. Some kernel threads are created in do_pre_smp_initcalls(), and shm_init() is called in do_initcalls(). Static initialization of shm_ids(init_ipc_ns).rw_mutex fixes the race. It fixes a kernel oops: Unable to handle kernel NULL pointer dereference at virtual address 00000000 ... [] (__down_write_nested+0x88/0xe0) from [] (exit_shm+0x28/0x48) [] (exit_shm+0x28/0x48) from [] (do_exit+0x59c/0x750) [] (do_exit+0x59c/0x750) from [] (____call_usermodehelper+0x13c/0x154) [] (____call_usermodehelper+0x13c/0x154) from [] (kernel_thread_exit+0x0/0x8) Code: 1afffffa e597c00c e58d0000 e587d00c (e58cd000) Reported-by: Manuel Lauss Reported-by: Richard Weinberger Reported-by: Marc Zyngier Tested-by: Manuel Lauss Tested-by: Richard Weinberger Tested-by: Marc Zyngier Signed-off-by: Vasiliy Kulikov Signed-off-by: Andrew Morton --- ipc/msgutil.c | 6 ++++++ ipc/shm.c | 11 ++++++++++- ipc/util.c | 30 +++++++++++++++++------------- ipc/util.h | 1 + 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/ipc/msgutil.c b/ipc/msgutil.c index 8b5ce5d3f3ef..6da67b60c7b3 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -20,6 +20,9 @@ DEFINE_SPINLOCK(mq_lock); +#define INIT_IPC_SHM_IDS(name) \ + { .rw_mutex = __RWSEM_INITIALIZER(name.rw_mutex), } + /* * The next 2 defines are here bc this is the only file * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE @@ -27,6 +30,9 @@ DEFINE_SPINLOCK(mq_lock); */ struct ipc_namespace init_ipc_ns = { .count = ATOMIC_INIT(1), + .ids = { + [IPC_SHM_IDS] = INIT_IPC_SHM_IDS(init_ipc_ns.ids[IPC_SHM_IDS]), + }, #ifdef CONFIG_POSIX_MQUEUE .mq_queues_max = DFLT_QUEUESMAX, .mq_msg_max = DFLT_MSGMAX, diff --git a/ipc/shm.c b/ipc/shm.c index b5bae9d945b6..890b38263f03 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -76,7 +76,16 @@ void shm_init_ns(struct ipc_namespace *ns) ns->shm_ctlmni = SHMMNI; ns->shm_rmid_forced = 0; ns->shm_tot = 0; - ipc_init_ids(&shm_ids(ns)); + + /* + * For init_ipc_ns shm_ids().rw_mutex is statically initialized + * as kernel threads should be able to use it in do_exit() before + * shm_init(), which is called on do_initcall() + */ + if (ns == &init_ipc_ns) + __ipc_init_ids(&shm_ids(ns)); + else + ipc_init_ids(&shm_ids(ns)); } /* diff --git a/ipc/util.c b/ipc/util.c index 75261a31d48d..673dde5f5a78 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -108,31 +108,35 @@ static int __init ipc_init(void) } __initcall(ipc_init); -/** - * ipc_init_ids - initialise IPC identifiers - * @ids: Identifier set - * - * Set up the sequence range to use for the ipc identifier range (limited - * below IPCMNI) then initialise the ids idr. - */ - -void ipc_init_ids(struct ipc_ids *ids) +void __ipc_init_ids(struct ipc_ids *ids) { - init_rwsem(&ids->rw_mutex); - ids->in_use = 0; ids->seq = 0; { int seq_limit = INT_MAX/SEQ_MULTIPLIER; if (seq_limit > USHRT_MAX) ids->seq_max = USHRT_MAX; - else - ids->seq_max = seq_limit; + else + ids->seq_max = seq_limit; } idr_init(&ids->ipcs_idr); } +/** + * ipc_init_ids - initialise IPC identifiers + * @ids: Identifier set + * + * Set up the sequence range to use for the ipc identifier range (limited + * below IPCMNI) then initialise the ids idr. + */ + +void ipc_init_ids(struct ipc_ids *ids) +{ + init_rwsem(&ids->rw_mutex); + __ipc_init_ids(ids); +} + #ifdef CONFIG_PROC_FS static const struct file_operations sysvipc_proc_fops; /** diff --git a/ipc/util.h b/ipc/util.h index 6f5c20bedaab..8bbcd9c850b3 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -80,6 +80,7 @@ struct seq_file; struct ipc_ids; void ipc_init_ids(struct ipc_ids *); +void __ipc_init_ids(struct ipc_ids *ids); #ifdef CONFIG_PROC_FS void __init ipc_init_proc_interface(const char *path, const char *header, int ids, int (*show)(struct seq_file *, void *)); -- 2.39.5