From 1b26fcdb748eb20a73f72900d7f5ab537b2809be Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 27 Mar 2019 00:08:41 +0100 Subject: Yama: mark local symbols as static sparse complains that Yama defines functions and a variable as non-static even though they don't exist in any header. Fix it by making them static. Signed-off-by: Jann Horn Reviewed-by: Mukesh Ojha Signed-off-by: James Morris --- security/yama/yama_lsm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 57cc60722dd3..06b14a57b0a4 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -206,7 +206,7 @@ static void yama_ptracer_del(struct task_struct *tracer, * yama_task_free - check for task_pid to remove from exception list * @task: task being removed */ -void yama_task_free(struct task_struct *task) +static void yama_task_free(struct task_struct *task) { yama_ptracer_del(task, task); } @@ -401,7 +401,7 @@ static int yama_ptrace_access_check(struct task_struct *child, * * Returns 0 if following the ptrace is allowed, -ve on error. */ -int yama_ptrace_traceme(struct task_struct *parent) +static int yama_ptrace_traceme(struct task_struct *parent) { int rc = 0; @@ -452,7 +452,7 @@ static int yama_dointvec_minmax(struct ctl_table *table, int write, static int zero; static int max_scope = YAMA_SCOPE_NO_ATTACH; -struct ctl_path yama_sysctl_path[] = { +static struct ctl_path yama_sysctl_path[] = { { .procname = "kernel", }, { .procname = "yama", }, { } -- cgit From 5c7e372caa35d303e414caeb64ee2243fd3cac3d Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 27 Mar 2019 16:39:38 +0100 Subject: security: don't use RCU accessors for cred->session_keyring sparse complains that a bunch of places in kernel/cred.c access cred->session_keyring without the RCU helpers required by the __rcu annotation. cred->session_keyring is written in the following places: - prepare_kernel_cred() [in a new cred struct] - keyctl_session_to_parent() [in a new cred struct] - prepare_creds [in a new cred struct, via memcpy] - install_session_keyring_to_cred() - from install_session_keyring() on new creds - from join_session_keyring() on new creds [twice] - from umh_keys_init() - from call_usermodehelper_exec_async() on new creds All of these writes are before the creds are committed; therefore, cred->session_keyring doesn't need RCU protection. Remove the __rcu annotation and fix up all existing users that use __rcu. Signed-off-by: Jann Horn Signed-off-by: James Morris --- security/keys/process_keys.c | 12 ++++-------- security/keys/request_key.c | 9 ++------- 2 files changed, 6 insertions(+), 15 deletions(-) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 9320424c4a46..bd7243cb4c85 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -227,6 +227,7 @@ static int install_process_keyring(void) * Install the given keyring as the session keyring of the given credentials * struct, replacing the existing one if any. If the given keyring is NULL, * then install a new anonymous session keyring. + * @cred can not be in use by any task yet. * * Return: 0 on success; -errno on failure. */ @@ -254,7 +255,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) /* install the keyring */ old = cred->session_keyring; - rcu_assign_pointer(cred->session_keyring, keyring); + cred->session_keyring = keyring; if (old) key_put(old); @@ -392,11 +393,8 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) /* search the session keyring */ if (ctx->cred->session_keyring) { - rcu_read_lock(); key_ref = keyring_search_aux( - make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1), - ctx); - rcu_read_unlock(); + make_key_ref(ctx->cred->session_keyring, 1), ctx); if (!IS_ERR(key_ref)) goto found; @@ -612,10 +610,8 @@ try_again: goto reget_creds; } - rcu_read_lock(); - key = rcu_dereference(ctx.cred->session_keyring); + key = ctx.cred->session_keyring; __key_get(key); - rcu_read_unlock(); key_ref = make_key_ref(key, 1); break; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 2f17d84d46f1..db72dc4d7639 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -142,12 +142,10 @@ static int call_sbin_request_key(struct key *authkey, void *aux) prkey = cred->process_keyring->serial; sprintf(keyring_str[1], "%d", prkey); - rcu_read_lock(); - session = rcu_dereference(cred->session_keyring); + session = cred->session_keyring; if (!session) session = cred->user->session_keyring; sskey = session->serial; - rcu_read_unlock(); sprintf(keyring_str[2], "%d", sskey); @@ -287,10 +285,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) /* fall through */ case KEY_REQKEY_DEFL_SESSION_KEYRING: - rcu_read_lock(); - dest_keyring = key_get( - rcu_dereference(cred->session_keyring)); - rcu_read_unlock(); + dest_keyring = key_get(cred->session_keyring); if (dest_keyring) break; -- cgit From 0b9dc6c9f01c4a726558b82a3b6082a89d264eb5 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 27 Mar 2019 16:55:08 +0100 Subject: keys: safe concurrent user->{session,uid}_keyring access The current code can perform concurrent updates and reads on user->session_keyring and user->uid_keyring. Add a comment to struct user_struct to document the nontrivial locking semantics, and use READ_ONCE() for unlocked readers and smp_store_release() for writers to prevent memory ordering issues. Fixes: 69664cf16af4 ("keys: don't generate user and user session keyrings unless they're accessed") Signed-off-by: Jann Horn Signed-off-by: James Morris --- security/keys/process_keys.c | 31 +++++++++++++++++-------------- security/keys/request_key.c | 5 +++-- 2 files changed, 20 insertions(+), 16 deletions(-) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index bd7243cb4c85..f05f7125a7d5 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -58,7 +58,7 @@ int install_user_keyrings(void) kenter("%p{%u}", user, uid); - if (user->uid_keyring && user->session_keyring) { + if (READ_ONCE(user->uid_keyring) && READ_ONCE(user->session_keyring)) { kleave(" = 0 [exist]"); return 0; } @@ -111,8 +111,10 @@ int install_user_keyrings(void) } /* install the keyrings */ - user->uid_keyring = uid_keyring; - user->session_keyring = session_keyring; + /* paired with READ_ONCE() */ + smp_store_release(&user->uid_keyring, uid_keyring); + /* paired with READ_ONCE() */ + smp_store_release(&user->session_keyring, session_keyring); } mutex_unlock(&key_user_keyring_mutex); @@ -340,6 +342,7 @@ void key_fsgid_changed(struct task_struct *tsk) key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) { key_ref_t key_ref, ret, err; + const struct cred *cred = ctx->cred; /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; @@ -353,9 +356,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) err = ERR_PTR(-EAGAIN); /* search the thread keyring first */ - if (ctx->cred->thread_keyring) { + if (cred->thread_keyring) { key_ref = keyring_search_aux( - make_key_ref(ctx->cred->thread_keyring, 1), ctx); + make_key_ref(cred->thread_keyring, 1), ctx); if (!IS_ERR(key_ref)) goto found; @@ -371,9 +374,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) } /* search the process keyring second */ - if (ctx->cred->process_keyring) { + if (cred->process_keyring) { key_ref = keyring_search_aux( - make_key_ref(ctx->cred->process_keyring, 1), ctx); + make_key_ref(cred->process_keyring, 1), ctx); if (!IS_ERR(key_ref)) goto found; @@ -392,9 +395,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) } /* search the session keyring */ - if (ctx->cred->session_keyring) { + if (cred->session_keyring) { key_ref = keyring_search_aux( - make_key_ref(ctx->cred->session_keyring, 1), ctx); + make_key_ref(cred->session_keyring, 1), ctx); if (!IS_ERR(key_ref)) goto found; @@ -413,9 +416,9 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx) } } /* or search the user-session keyring */ - else if (ctx->cred->user->session_keyring) { + else if (READ_ONCE(cred->user->session_keyring)) { key_ref = keyring_search_aux( - make_key_ref(ctx->cred->user->session_keyring, 1), + make_key_ref(READ_ONCE(cred->user->session_keyring), 1), ctx); if (!IS_ERR(key_ref)) goto found; @@ -602,7 +605,7 @@ try_again: goto error; goto reget_creds; } else if (ctx.cred->session_keyring == - ctx.cred->user->session_keyring && + READ_ONCE(ctx.cred->user->session_keyring) && lflags & KEY_LOOKUP_CREATE) { ret = join_session_keyring(NULL); if (ret < 0) @@ -616,7 +619,7 @@ try_again: break; case KEY_SPEC_USER_KEYRING: - if (!ctx.cred->user->uid_keyring) { + if (!READ_ONCE(ctx.cred->user->uid_keyring)) { ret = install_user_keyrings(); if (ret < 0) goto error; @@ -628,7 +631,7 @@ try_again: break; case KEY_SPEC_USER_SESSION_KEYRING: - if (!ctx.cred->user->session_keyring) { + if (!READ_ONCE(ctx.cred->user->session_keyring)) { ret = install_user_keyrings(); if (ret < 0) goto error; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index db72dc4d7639..75d87f9e0f49 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -293,11 +293,12 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) /* fall through */ case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: dest_keyring = - key_get(cred->user->session_keyring); + key_get(READ_ONCE(cred->user->session_keyring)); break; case KEY_REQKEY_DEFL_USER_KEYRING: - dest_keyring = key_get(cred->user->uid_keyring); + dest_keyring = + key_get(READ_ONCE(cred->user->uid_keyring)); break; case KEY_REQKEY_DEFL_GROUP_KEYRING: -- cgit From d1a0846006e4325cc951ca0b05c02ed1d0865006 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 15 Mar 2019 16:00:25 -0500 Subject: security: inode: fix a missing check for securityfs_create_file securityfs_create_file may fail. The fix checks its status and returns the error code upstream if it fails. Signed-off-by: Kangjie Lu Signed-off-by: James Morris --- security/inode.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'security') diff --git a/security/inode.c b/security/inode.c index b7772a9b315e..667f8b15027d 100644 --- a/security/inode.c +++ b/security/inode.c @@ -339,6 +339,11 @@ static int __init securityfs_init(void) #ifdef CONFIG_SECURITY lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL, &lsm_ops); + if (IS_ERR(lsm_dentry)) { + unregister_filesystem(&fs_type); + sysfs_remove_mount_point(kernel_kobj, "security"); + return PTR_ERR(lsm_dentry); + } #endif return 0; } -- cgit From ecb8e74dac1aaeea4005c9d8f21337f9423f3d8b Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 27 Mar 2019 13:20:18 +0530 Subject: Yama: mark function as static Sparse complains yama_task_prctl can be static. Fix it by making it static. Signed-off-by: Mukesh Ojha Signed-off-by: James Morris --- security/yama/yama_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 06b14a57b0a4..efac68556b45 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -222,7 +222,7 @@ static void yama_task_free(struct task_struct *task) * Return 0 on success, -ve on error. -ENOSYS is returned when Yama * does not handle the given option. */ -int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, +static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { int rc = -ENOSYS; -- cgit From fe9fd2ef383c2f5883fcd3f7adce0de9ce2556ff Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 10 Apr 2019 14:59:20 -0700 Subject: Revert "security: inode: fix a missing check for securityfs_create_file" This reverts commit d1a0846006e4325cc951ca0b05c02ed1d0865006. From Al Viro: "Rather bad way to do it - generally, register_filesystem() should be the last thing done by initialization. Any modular code that does unregister_filesystem() on failure exit is flat-out broken; here it's not instantly FUBAR, but it's a bloody bad example. What's more, why not let simple_fill_super() do it? Just static int fill_super(struct super_block *sb, void *data, int silent) { static const struct tree_descr files[] = { {"lsm", &lsm_ops, 0444}, {""} }; and to hell with that call of securityfs_create_file() and all its failure handling..." Signed-off-by: James Morris --- security/inode.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'security') diff --git a/security/inode.c b/security/inode.c index 667f8b15027d..b7772a9b315e 100644 --- a/security/inode.c +++ b/security/inode.c @@ -339,11 +339,6 @@ static int __init securityfs_init(void) #ifdef CONFIG_SECURITY lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL, &lsm_ops); - if (IS_ERR(lsm_dentry)) { - unregister_filesystem(&fs_type); - sysfs_remove_mount_point(kernel_kobj, "security"); - return PTR_ERR(lsm_dentry); - } #endif return 0; } -- cgit