diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/fork.c | 12 | ||||
| -rw-r--r-- | kernel/nsproxy.c | 2 | ||||
| -rw-r--r-- | kernel/nstree.c | 80 | ||||
| -rw-r--r-- | kernel/panic.c | 5 | ||||
| -rw-r--r-- | kernel/power/energy_model.c | 4 | ||||
| -rw-r--r-- | kernel/power/suspend.c | 20 | ||||
| -rw-r--r-- | kernel/signal.c | 5 | ||||
| -rw-r--r-- | kernel/trace/Kconfig | 1 | ||||
| -rw-r--r-- | kernel/trace/bpf_trace.c | 15 | ||||
| -rw-r--r-- | kernel/ucount.c | 28 | ||||
| -rw-r--r-- | kernel/user_namespace.c | 5 | ||||
| -rw-r--r-- | kernel/watch_queue.c | 2 |
12 files changed, 103 insertions, 76 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 18b6dd851642..8b108ed37152 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -971,10 +971,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) dup_user_cpus_ptr(tsk, orig, node); /* - * One for the user space visible state that goes away when reaped. - * One for the scheduler. + * Nobody can keep the task while copy_process() may still fail, which + * frees it no matter what. */ - refcount_set(&tsk->rcu_users, 2); + refcount_set(&tsk->rcu_users, 0); /* One for the rcu users */ refcount_set(&tsk->usage, 1); #ifdef CONFIG_BLK_DEV_IO_TRACE @@ -2510,6 +2510,12 @@ __latent_entropy struct task_struct *copy_process( /* No more failure paths after this point. */ /* + * One for the user space visible state that goes away when reaped. + * One for the scheduler. + */ + refcount_set(&p->rcu_users, 2); + + /* * Copy seccomp details explicitly here, in case they were changed * before holding sighand lock. */ diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 454427b09ed9..2dcf105e8ee2 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -355,6 +355,7 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset) { struct task_struct *me = current; + nsset->flags = flags; nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs); if (IS_ERR(nsset->nsproxy)) return PTR_ERR(nsset->nsproxy); @@ -375,7 +376,6 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset) goto out; } - nsset->flags = flags; return 0; out: diff --git a/kernel/nstree.c b/kernel/nstree.c index 831f279d174a..a756ae2e7881 100644 --- a/kernel/nstree.c +++ b/kernel/nstree.c @@ -408,15 +408,12 @@ struct klistns { u32 ns_type; struct user_namespace *user_ns; bool userns_capable; - struct ns_common *first_ns; }; static void __free_klistns_free(const struct klistns *kls) { if (kls->user_ns_id != LISTNS_CURRENT_USER) put_user_ns(kls->user_ns); - if (kls->first_ns && kls->first_ns->ops) - kls->first_ns->ops->put(kls->first_ns); } #define NS_ALL (PID_NS | USER_NS | MNT_NS | UTS_NS | IPC_NS | NET_NS | CGROUP_NS | TIME_NS) @@ -459,10 +456,13 @@ static inline int prepare_klistns(struct klistns *kls, struct ns_id_req *kreq, } /* - * Lookup a namespace owned by owner with id >= ns_id. - * Returns the namespace with the smallest id that is >= ns_id. + * Lookup a namespace owned by owner with id > ns_id. + * Returns the namespace with the smallest id that is > ns_id. + * + * No reference is taken. The caller must hold rcu_read_lock() and may only + * use the result as the starting point of an RCU list walk. */ -static struct ns_common *lookup_ns_owner_at(u64 ns_id, struct ns_common *owner) +static struct ns_common *lookup_ns_owner_after(u64 ns_id, struct ns_common *owner) { struct ns_common *ret = NULL; struct rb_node *node; @@ -476,18 +476,14 @@ static struct ns_common *lookup_ns_owner_at(u64 ns_id, struct ns_common *owner) struct ns_common *ns; ns = node_to_ns_owner(node); - if (ns_id <= ns->ns_id) { + if (ns_id < ns->ns_id) { ret = ns; - if (ns_id == ns->ns_id) - break; node = node->rb_left; } else { node = node->rb_right; } } - if (ret) - ret = ns_get_unless_inactive(ret); return ret; } @@ -560,25 +556,27 @@ static ssize_t do_listns_userns(struct klistns *kls) return -EINVAL; kls->user_ns = to_user_ns(ns); - /* - * Use the rbtree to find the first namespace we care about and - * then use it's list entry to iterate from there. - */ - if (kls->last_ns_id) { - kls->first_ns = lookup_ns_owner_at(kls->last_ns_id + 1, ns); - if (!kls->first_ns) - return -ENOENT; - first_ns = kls->first_ns; - } - ret = 0; head = &to_ns_common(kls->user_ns)->ns_owner_root.ns_list_head; kls->userns_capable = may_see_all_namespaces(); rcu_read_lock(); - if (!first_ns) + /* + * Use the rbtree to find the first namespace we care about and + * then use it's list entry to iterate from there. That namespace + * is filtered like any other, so the walk doesn't end early just + * because it is inactive or already dying. + */ + if (kls->last_ns_id) { + first_ns = lookup_ns_owner_after(kls->last_ns_id, ns); + if (!first_ns) { + rcu_read_unlock(); + return -ENOENT; + } + } else { first_ns = list_entry_rcu(head->next, typeof(*first_ns), ns_owner_node.ns_list_entry); + } ns = first_ns; list_for_each_entry_from_rcu(ns, head, ns_owner_node.ns_list_entry) { @@ -613,10 +611,13 @@ static ssize_t do_listns_userns(struct klistns *kls) } /* - * Lookup a namespace with id >= ns_id in either the unified tree or a type-specific tree. - * Returns the namespace with the smallest id that is >= ns_id. + * Lookup a namespace with id > ns_id in either the unified tree or a type-specific tree. + * Returns the namespace with the smallest id that is > ns_id. + * + * No reference is taken. The caller must hold rcu_read_lock() and may only + * use the result as the starting point of an RCU list walk. */ -static struct ns_common *lookup_ns_id_at(u64 ns_id, int ns_type) +static struct ns_common *lookup_ns_id_after(u64 ns_id, int ns_type) { struct ns_common *ret = NULL; struct ns_tree_root *ns_tree = NULL; @@ -643,21 +644,14 @@ static struct ns_common *lookup_ns_id_at(u64 ns_id, int ns_type) else ns = node_to_ns_unified(node); - if (ns_id <= ns->ns_id) { - if (ns_type) - ret = node_to_ns(node); - else - ret = node_to_ns_unified(node); - if (ns_id == ns->ns_id) - break; + if (ns_id < ns->ns_id) { + ret = ns; node = node->rb_left; } else { node = node->rb_right; } } - if (ret) - ret = ns_get_unless_inactive(ret); return ret; } @@ -707,13 +701,6 @@ static ssize_t do_listns(struct klistns *kls) return -EINVAL; } - if (kls->last_ns_id) { - kls->first_ns = lookup_ns_id_at(kls->last_ns_id + 1, ns_type); - if (!kls->first_ns) - return -ENOENT; - first_ns = kls->first_ns; - } - ret = 0; if (ns_tree) head = &ns_tree->ns_list_head; @@ -722,8 +709,15 @@ static ssize_t do_listns(struct klistns *kls) rcu_read_lock(); - if (!first_ns) + if (kls->last_ns_id) { + first_ns = lookup_ns_id_after(kls->last_ns_id, ns_type); + if (!first_ns) { + rcu_read_unlock(); + return -ENOENT; + } + } else { first_ns = first_ns_common(head, ns_tree); + } for (ns = first_ns; !ns_common_is_head(ns, head, ns_tree) && nr_ns_ids; ns = next_ns_common(ns, ns_tree)) { diff --git a/kernel/panic.c b/kernel/panic.c index b824b68fcb08..574ee5a12f92 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -825,7 +825,6 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { TAINT_FLAG(RANDSTRUCT, 'T', ' '), TAINT_FLAG(TEST, 'N', ' '), TAINT_FLAG(FWCTL, 'J', ' '), - TAINT_FLAG(FORCED_BIND, 'Y', ' '), }; #undef TAINT_FLAG @@ -862,9 +861,9 @@ static void print_tainted_seq(struct seq_buf *s, bool verbose) * exact size is allocated dynamically; the initial buffer remains * as a fallback if allocation fails. * - * The verbose taint string currently requires up to 344 characters. + * The verbose taint string currently requires up to 327 characters. */ -#define INIT_TAINT_BUF_MAX 370 +#define INIT_TAINT_BUF_MAX 350 static char init_taint_buf[INIT_TAINT_BUF_MAX] __initdata; static char *taint_buf __refdata = init_taint_buf; diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index a76089e2ce8c..d19a65461ae8 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -439,7 +439,9 @@ static int em_create_pd(struct device *dev, int nr_states, cpumask_copy(em_span_cpus(pd), cpus); } else { - pd = kzalloc_obj(*pd); + /* Readers expect pd->cpus to exist and remain empty. */ + pd = kzalloc_flex(*pd, cpus, + BITS_TO_LONGS(large_cpumask_bits)); if (!pd) return -ENOMEM; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 57c44268698f..32c055df02da 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -306,20 +306,24 @@ static void platform_resume_finish(suspend_state_t state) static int platform_suspend_begin(suspend_state_t state) { - if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->begin) - return s2idle_ops->begin(); - else if (suspend_ops && suspend_ops->begin) + if (state == PM_SUSPEND_TO_IDLE) { + if (s2idle_ops && s2idle_ops->begin) + return s2idle_ops->begin(); + } else if (suspend_ops && suspend_ops->begin) { return suspend_ops->begin(state); - else - return 0; + } + + return 0; } static void platform_resume_end(suspend_state_t state) { - if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->end) - s2idle_ops->end(); - else if (suspend_ops && suspend_ops->end) + if (state == PM_SUSPEND_TO_IDLE) { + if (s2idle_ops && s2idle_ops->end) + s2idle_ops->end(); + } else if (suspend_ops && suspend_ops->end) { suspend_ops->end(); + } } static void platform_recover(suspend_state_t state) diff --git a/kernel/signal.c b/kernel/signal.c index cb6ec7054eee..dd770e68faee 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -684,12 +684,13 @@ static int dequeue_synchronous_signal(kernel_siginfo_t *info) return 0; /* - * Return the first synchronous signal in the queue. + * Return the first unblocked synchronous signal in the queue. */ list_for_each_entry(q, &pending->list, list) { /* Synchronous signals have a positive si_code */ if ((q->info.si_code > SI_USER) && - (sigmask(q->info.si_signo) & SYNCHRONOUS_MASK)) { + (sigmask(q->info.si_signo) & SYNCHRONOUS_MASK) && + !sigismember(&tsk->blocked, q->info.si_signo)) { sync = q; goto next; } diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index c41397eccee5..35d043b19332 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -1337,6 +1337,7 @@ source "kernel/trace/rv/Kconfig" config TRACE_REMOTE bool + select TRACING config SIMPLE_RING_BUFFER bool diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 14adfb85fdb2..2aec56d7f3d3 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -974,6 +974,10 @@ BTF_ID(func, vfs_getattr) BTF_ID(func, filp_close) BTF_SET_END(btf_allowlist_d_path) +BTF_SET_START(btf_allowlist_d_path_fentry) +BTF_ID(func, filp_close_sync) +BTF_SET_END(btf_allowlist_d_path_fentry) + static bool bpf_d_path_allowed(const struct bpf_prog *prog) { if (prog->type == BPF_PROG_TYPE_TRACING && @@ -983,8 +987,15 @@ static bool bpf_d_path_allowed(const struct bpf_prog *prog) if (prog->type == BPF_PROG_TYPE_LSM) return bpf_lsm_is_sleepable_hook(prog->aux->attach_btf_id); - return btf_id_set_contains(&btf_allowlist_d_path, - prog->aux->attach_btf_id); + if (btf_id_set_contains(&btf_allowlist_d_path, + prog->aux->attach_btf_id)) + return true; + + if (btf_id_set_contains(&btf_allowlist_d_path_fentry, + prog->aux->attach_btf_id)) + return prog->expected_attach_type == BPF_TRACE_FENTRY; + + return false; } BTF_ID_LIST_SINGLE(bpf_d_path_btf_ids, struct, path) diff --git a/kernel/ucount.c b/kernel/ucount.c index ec8b1445e287..94b70502dc9d 100644 --- a/kernel/ucount.c +++ b/kernel/ucount.c @@ -285,9 +285,12 @@ static void do_dec_rlimit_put_ucounts(struct ucounts *ucounts, { struct ucounts *iter, *next; for (iter = ucounts; iter != last; iter = next) { - long dec = atomic_long_sub_return(1, &iter->rlimit[type]); - WARN_ON_ONCE(dec < 0); + long dec; + + /* Our count pins @iter; once it is dropped @iter may be freed. */ next = iter->ns->ucounts; + dec = atomic_long_sub_return(1, &iter->rlimit[type]); + WARN_ON_ONCE(dec < 0); if (dec == 0) put_ucounts(iter); } @@ -308,23 +311,26 @@ long inc_rlimit_get_ucounts(struct ucounts *ucounts, enum rlimit_type type, for (iter = ucounts; iter; iter = iter->ns->ucounts) { long new = atomic_long_add_return(1, &iter->rlimit[type]); - if (new < 0 || new > max) + /* + * Take the extra reference on the 0 -> 1 transition before the + * limit check: a rejected walk must not leave a concurrent one + * holding the surviving increment with no reference. + */ + if (new == 1 && !get_ucounts(iter)) goto dec_unwind; + if (new < 0 || new > max) { + /* undo iter too, releasing the reference taken above */ + do_dec_rlimit_put_ucounts(ucounts, iter->ns->ucounts, type); + return 0; + } if (iter == ucounts) ret = new; if (!override_rlimit) max = get_userns_rlimit_max(iter->ns, type); - /* - * Grab an extra ucount reference for the caller when - * the rlimit count was previously 0. - */ - if (new != 1) - continue; - if (!get_ucounts(iter)) - goto dec_unwind; } return ret; dec_unwind: + /* get_ucounts() failed for iter: undo iter without a put */ dec = atomic_long_sub_return(1, &iter->rlimit[type]); WARN_ON_ONCE(dec < 0); do_dec_rlimit_put_ucounts(ucounts, iter, type); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 50c374ed4687..cd748e1cbace 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -46,8 +46,11 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns) { /* Start with the same capabilities as init but useless for doing * anything as the capabilities are bound to the new user namespace. + * The exec securebits grant nothing, they are restrictions the task + * placed on itself. Keep them and their locks. */ - cred->securebits = SECUREBITS_DEFAULT; + cred->securebits = SECUREBITS_DEFAULT | + (cred->securebits & (SECURE_ALL_UNPRIVILEGED | SECURE_ALL_UNPRIVILEGED << 1)); cred->cap_inheritable = CAP_EMPTY_SET; cred->cap_permitted = CAP_FULL_SET; cred->cap_effective = CAP_FULL_SET; diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c index 932a88ac1f39..453dcf613205 100644 --- a/kernel/watch_queue.c +++ b/kernel/watch_queue.c @@ -212,7 +212,7 @@ void __post_watch_notification(struct watch_list *wlist, if (watch->id != id) continue; n->info &= ~WATCH_INFO_ID; - n->info |= watch->info_id; + n->info |= watch->info_id & WATCH_INFO_ID; wqueue = rcu_dereference(watch->queue); wf = rcu_dereference(wqueue->filter); |
