Description of problem:
Alexei Dobryanov has reported the following kernel utrace related issue
(BZ#245735):
1. late ptrace_may_attach() check
static int ptrace_attach(struct task_struct *task)
{
...
engine = utrace_attach(task, (UTRACE_ATTACH_CREATE
| UTRACE_ATTACH_EXCLUSIVE
| UTRACE_ATTACH_MATCH_OPS),
&ptrace_utrace_ops, 0);
[error checking]
if (ptrace_may_attach(task)) {
[more attaching process]
Doing may attach check there is asking for trouble, because utrace_attach()
will happily create and modify "struct utrace *" and create and attach
engines to it on task you don't have permissions. Order should be reverted.
That's easy.
2. race around &dead_engine_ops setting...
I originally thought #1 would lead to memory leaks, however, written dumb
PTRACE_ATTACH'er gave much more amazing results.
The following program quickly (1 sec) oopses kernel when run against
process you normally can't attach to (like normal user to getty processes)
#include <stdlib.h>
#include <sys/ptrace.h>
int main(int argc, char *argv[])
{
pid_t pid = atoi(argv[1]);
while (1)
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
return 0;
}
Unable to handle kernel NULL pointer dereference at 0000000000000000 RIP:
[<0000000000000000>]
[<ffffffff8005f1cd>] report_quiescent+0x36/0x154
[<ffffffff8005f316>] utrace_quiescent+0x2b/0x238
[<ffffffff800601e9>] utrace_get_signal+0x45d/0x4c0
[<ffffffff80039c6f>] get_signal_to_deliver+0x169/0x47a
[<ffffffff80008f5a>] do_notify_resume+0xd0/0x7e2
[<ffffffff80203673>] _spin_unlock_irqrestore+0x3f/0x45
[<ffffffff80051d71>] trace_hardirqs_on+0x11b/0x13f
[<ffffffff801400c0>] tty_read+0x81/0xc7
[<ffffffff80202ede>] trace_hardirqs_on_thunk+0x35/0x37
[<ffffffff80051d71>] trace_hardirqs_on+0x11b/0x13f
[<ffffffff80009b43>] sysret_signal+0x21/0x31
[<ffffffff80009deb>] ptregscall_common+0x67/0xac
This is a race we chatted with Roland about:
http://marc.info/?l=linux-kernel&m=117863520707703&w=2> engine's flags and ops settings in utrace_detach() and acting on them in
> report_quiescent():> utrace_detach() report_quiescent()
> --------------- ------------------
> [utrace lock held] [utrace lock is not held]> engine->flags =
> UTRACE_EVENT(QUIESCE) | UTRACE_ACTION_QUIESCE;> if (engine->flags & UTRACE_EVENT(QUIESCE))
> REPORT(report_quiesce);> rcu_assign_pointer(engine->ops, &dead_engine_ops);> At the moment of REPORT call engine's ops are still "live" ptrace ops
> which do not have ->report_quiesce callback. So, there will oops while
> calling function at NULL address. "Dead" ptrace engine ops do have dummy
> callback but it wasn't yet glued.
Obviously, patch #1 won't fix this.
3. Looks like nobody filed double free at utrace aka
oops at __rcu_process_callbacks() against RHEL5 kernel.
It's bug https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=207002
against FC6, but, hey, every utrace version has it. Test program attached.
Every user can trigger it.