Skip to content

futex: gate get_robust_list behind a ptrace access check#13241

Open
ibondarenko1 wants to merge 1 commit into
google:masterfrom
ibondarenko1:hardening/get-robust-list-cantrace
Open

futex: gate get_robust_list behind a ptrace access check#13241
ibondarenko1 wants to merge 1 commit into
google:masterfrom
ibondarenko1:hardening/get-robust-list-cantrace

Conversation

@ibondarenko1
Copy link
Copy Markdown

Problem

get_robust_list(2) (GetRobustList in pkg/sentry/syscalls/linux/sys_futex.go) takes a caller-supplied tid, resolves it with PIDNamespace().TaskWithID(), and copies out that task's robust-list head pointer:

ot := t
if tid != 0 {
	if ot = t.PIDNamespace().TaskWithID(kernel.ThreadID(tid)); ot == nil {
		return 0, nil, linuxerr.ESRCH
	}
}

// Copy out head pointer.
head := t.Arch().Native(uintptr(ot.GetRobustList()))

There is no permission check between resolving ot and reading ot.GetRobustList(). Any task can read the robust-list head of any other task in its PID namespace, including a task running as a different user. The head is a pointer into the target task's address space, so this discloses an address-space-layout pointer across a process boundary.

Linux behavior

Linux gates this. kernel/futex/syscalls.c:do_get_robust_list() returns -EPERM unless ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS) passes.

In-tree precedent

process_vm_readv(2) is the other syscall in this tree that reaches another task by tid, and it already applies the equivalent gate (pkg/sentry/syscalls/linux/sys_process_vm.go):

if !t.CanTrace(remoteTask, true /* attach */) {
	return 0, nil, linuxerr.EPERM
}

Change

Add a Task.CanTrace check in read mode (attach == false, matching PTRACE_MODE_READ) before reading another task's robust-list head, so a task cannot inspect the robust list of a task it cannot trace. Reading one's own list (ot == t, including tid == 0) is unaffected.

Three added lines, no new imports.

Scope

Hardening / Linux-parity fix. The disclosure is confined to the caller's PID namespace and does not cross the gVisor sandbox boundary.

get_robust_list(2) resolves a caller-supplied tid and copies out that
task's robust-list head pointer with no permission check, so a task can
read the robust-list head of any other task in its PID namespace,
including a task owned by a different user.

Linux gates this in kernel/futex/syscalls.c:do_get_robust_list(), which
returns EPERM unless ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS)
passes. process_vm_readv(2) already applies the equivalent Task.CanTrace
check. Apply the same check before reading another task's robust-list
head.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants