cred: copy_process() should clear child->replacement_session_keyring
Subsystems (6)
cover cred: copy_process() should clear child->replacement_session_keyring
Hello.
Looks trivial, but I do not know how to test and I don't understand this code. David, please review ;)
Note: initially this was 1/2. The usage of >replacement_session_keyring is racy, although the problem is minor. I think that instead of fixing a lot of key_replace_session_keyring's callers in arch/ we can add the simple abstraction layer and kill ->replacement_session_keyring. I'll try to send the patches tomorrow.
Btw. exit_creds() does validate_creds(cred) right before put_cred(cred), without alter_cred_subscribers() in between this looks unneeded?
Oleg.
PATCH 1/1 cred: copy_process() should clear child->replacement_session_keyring
keyctl_session_to_parent(task) sets ->replacement_session_keyring, it should be processed and cleared by key_replace_session_keyring().
However, this task can fork before it notices TIF_NOTIFY_RESUME and the new child gets the bogus ->replacement_session_keyring copied by dup_task_struct(). This is obviously wrong and, if nothing else, this leads to put_cred(already_freed_cred).
change copy_creds() to clear this member. If copy_process() fails before this point the wrong ->replacement_session_keyring doesn't matter, exit_creds() won't be called.
--- kernel/cred.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
kernel/cred.c +2 -0
@@ -386,6 +386,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)struct cred *new;int ret; + p->replacement_session_keyring =NULL ; + if (#ifdef CONFIG_KEYS !p->cred->thread_keyring &&
Signature · 1 line
1.5.5.1
cover task_work_queue: add generic process-context callbacks
Hello.
I didn't read the "v3.4-rc2 out-of-memory problems" thread yet, but I noticed how much Linus loves the yet-another-random-notifier-for-some-random-reason idea.
Yet I am going to suggest another one, please see the patch.
But. To defence this change, please note that de-facto the notifier is already here.
Quoted 6 lines · Oleg Nesterov
Note: initially this was 1/2. The usage of >replacement_session_keyring is racy, although the problem is minor. I think that instead of fixing a lot of key_replace_session_keyring's callers in
arch/we can add the simple abstraction layer and kill ->replacement_session_keyring. I'll try to send the patches tomorrow.
Yes. the callers of key_replace_session_keyring() and keyctl_session_to_parent() lack the barriers and can race with each other. And keyctl_session_to_parent() probably needs kick_process(). Yes, the problem is minor, but still.
To me, the main problem is task->->replacement_session_keyring itself. I mean, imho it should not be that limited, we should generalize this logic.
And potentially (I hope) it can have more users besides KEYCTL_SESSION_TO_PARENT. Say, we can remove do_exit()->exit_irq_thread() and task->irq_thread. The creator of irq thread can do task_queue_work(exit_irq_thread).
What do you think?
Oleg.
RFC PATCH 1/1 task_work_queue: add generic process-context callbacks
Provide a simple mechanism that allows running code in the (nonatomic) context of the arbitrary task.
The caller does task_work_queue(task, task_work) and this task executes task_work->func() either from do_notify_resume() or from do_exit(). The callback can rely on PF_EXITING to detect the latter case.
"struct task_work" can be embedded in another struct, still it has "void *data" to handle the most common/simple case.
This allows us to kill the ->replacement_session_keyring hack, and potentially this can have more users.
Performance-wise, this adds 2 "unlikely(!hlist_empty())" checks into tracehook_notify_resume() and do_exit(). But at the same time we can remove the "replacement_session_keyring != NULL" checks from arch/*/signal.c and exit_creds().
Note: task_work_queue/task_work_run abuses ->pi_lock. This is only because this lock is already used by lookup_pi_state() to synchronize with do_exit() setting PF_EXITING. Fortunately the scope of this lock in task_work.c is really tiny, and the code is unlikely anyway.
Todo:
- move clear_thread_flag(TIF_NOTIFY_RESUME) from arch/ to tracehook_notify_resume()
- rename tracehook_notify_resume() and move it into linux/task_work.h
- m68k and xtensa don't have TIF_NOTIFY_RESUME and thus they can't use this feature.
However, ->replacement_session_keyring equally needs this logic, task_work_queue() is not worse.
--- include/linux/sched.h | 2 + include/linux/task_work.h | 39 +++++++++++++++++++++++++++++ include/linux/tracehook.h | 10 +++++++- kernel/Makefile | 2 +- kernel/exit.c | 5 +++- kernel/fork.c | 1 + kernel/task_work.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 include/linux/task_work.h create mode 100644 kernel/task_work.c
include/linux/sched.h +2 -0
@@ -1445,6 +1445,8 @@ struct task_struct {int (*notifier)(void *priv);void *notifier_data;sigset_t *notifier_mask; +struct hlist_head task_works; +struct audit_context *audit_context;#ifdef CONFIG_AUDITSYSCALL uid_t loginuid;
include/linux/task_work.h new file +39 -0
@@ -0,0 +1,39 @@ +#ifndef _LINUX_TASK_WORK_H +#define _LINUX_TASK_WORK_H 1 + +#include <linux/sched.h> + +struct task_work { +struct hlist_node hlist; +void (*func)(struct task_work *); +void *data; +}; + +static inline void init_task_work (struct task_work *twork, +void (*func)(struct task_work *),void *data) +{ + twork->func = func; + twork->data = data; +} + +#ifdef TIF_NOTIFY_RESUME +int task_work_queue (struct task_struct *task,struct task_work *twork); +void task_work_run (struct task_struct *task); +#else +static inline +int task_work_queue (struct task_struct *task,struct task_work *twork) +{ +return -ENOTSUPP ; +} +static inline void task_work_run (struct task_struct *task) +{ +} +#endif /* TIF_NOTIFY_RESUME */ + +static inline void exit_task_work (struct task_struct *task) +{ +if (unlikely (!hlist_empty (&task->task_works))) +task_work_run (task); +} + +#endif /* _LINUX_TASK_WORK_H */
include/linux/tracehook.h +9 -1
@@ -46,7 +46,7 @@#ifndef _LINUX_TRACEHOOK_H#define _LINUX_TRACEHOOK_H 1 -#include <linux/sched.h> +#include <linux/task_work.h> #include <linux/ptrace.h> #include <linux/security.h> struct linux_binprm ;
@@ -184,6 +184,14 @@ static inline void set_notify_resume(struct task_struct *task)*/ static inline void tracehook_notify_resume (struct pt_regs *regs) { +/* +* The caller just cleared TIF_NOTIFY_RESUME. This barrier +* pairs with task_work_queue()->set_notify_resume() after +* hlist_add_head(task->task_works); +*/ +smp_mb__after_clear_bit (); +if (unlikely (!hlist_empty (¤t->task_works))) +task_work_run (current); }#endif /* TIF_NOTIFY_RESUME */
kernel/Makefile +1 -1
@@ -10,7 +10,7 @@ obj-y = fork.o exec_domain.o panic.o printk.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ notifier.o ksysfs.o cred.o \ - async.o range.o groups.o + async.o range.o groups.o task_work.o ifdef CONFIG_FUNCTION_TRACER # Do not trace debug files and internal ftrace files
kernel/exit.c +4 -1
@@ -946,11 +946,14 @@ void do_exit(long code)exit_signals (tsk);/* sets PF_EXITING */ /* * tsk->flags are checked in the futex code to protect against -* an exiting task cleaning up the robust pi futexes. +* an exiting task cleaning up the robust pi futexes, and in +* task_work_queue() to avoid the race with exit_task_work(). */ smp_mb ();raw_spin_unlock_wait (&tsk->pi_lock); +exit_task_work (tsk); +exit_irq_thread ();if (unlikely (in_atomic ()))
kernel/fork.c +1 -0
@@ -1380,6 +1380,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,*/ p->group_leader = p;INIT_LIST_HEAD (&p->thread_group); +INIT_HLIST_HEAD (&p->task_works);/* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible
kernel/task_work.c new file +59 -0
59 lines not on this page · raw message
Signature · 1 line
1.5.5.1
Quoted 8 lines · Oleg Nesterov
keyctl_session_to_parent(task)sets ->replacement_session_keyring, it should be processed and cleared bykey_replace_session_keyring().However, this task can fork before it notices
TIF_NOTIFY_RESUMEand the new child gets the bogus ->replacement_session_keyring copied bydup_task_struct(). This is obviously wrong and, if nothing else, this leads toput_cred(already_freed_cred).
Quoted 6 lines
change
copy_creds()to clear this member. Ifcopy_process()fails before this point the wrong ->replacement_session_keyring doesn't matter,exit_creds()won't be called.Cc <[email protected]>Signed-off-by Oleg Nesterov <[email protected]>