From 8189f670a8d1d1aa1e8fafabe3a0fa09cfd5da26 Mon Sep 17 00:00:00 2001 From: Nick Alcock Date: Thu, 17 Jan 2013 14:25:40 +0000 Subject: [PATCH] ptrace: Add PTRACE_GETMAPFD. This request returns (in 'data') the file descriptor backing the mapping that covers a given 'addr'. This works even if the mapped file is unlinked and not open anywhere besides this mapping. New errors: -BADFD if the address is an anonymous mapping -EFAULT if the address is not mapped at all -PERM if the LSM does not permit the receipt of this file descriptor Note that because the original fd is returned, you can access portions of it outside the range of the original mapping (the use case for this, involving acquiring ELF headers for executables and shared libraries that were unlinked after mapping, relies on this). This does not introduce a security hole because in order for the ptraced process to mmap any file in the first place it must have had an fd to it, and the ptracee could have accessed the outside-original-mapping portions at that point, or simply forced the ptraced process to send it the fd via a Unix-domain socket and held on to it. There is no danger that an execute-only process can be read by this mechanism either, since you cannot PTRACE_ATTACH to such a process. (Shared libraries cannot be execute-only at all.) PTRACE_GETMAPFD is provisionally of value 0x42A5, in the architecture- independent addition range, out-of-the-way so as not to collide with other additions. Signed-off-by: Nick Alcock --- include/uapi/linux/ptrace.h | 9 +++++++ kernel/ptrace.c | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index cf1019e15f5b..d6aa5375eb5a 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h @@ -49,6 +49,15 @@ #define PTRACE_GETREGSET 0x4204 #define PTRACE_SETREGSET 0x4205 +/* + * Get an fd corresponding to the mapping in which addr is located, and put it + * in data. Returns -EBADFD if this mapping has no fd corresponding to it, and + * (as usual) -EFAULT if this address is not mapped. + * + * O_CLOEXEC is set on this fd by default. + */ +#define PTRACE_GETMAPFD 0x42A5 /* Oracle addition: out-of-the-way */ + #define PTRACE_SEIZE 0x4206 #define PTRACE_INTERRUPT 0x4207 #define PTRACE_LISTEN 0x4208 diff --git a/kernel/ptrace.c b/kernel/ptrace.c index c8e0e050a36a..5c511f403e42 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -796,6 +798,52 @@ static int ptrace_regset(struct task_struct *task, int req, unsigned int type, EXPORT_SYMBOL_GPL(task_user_regset_view); #endif +static int ptrace_getmapfd(struct task_struct *child, unsigned long addr, + unsigned long data) +{ + void __user *datavp = (void __user *) data; + unsigned long __user *datalp = datavp; + int ret; + + struct mm_struct *mm; + struct vm_area_struct *vma; + struct files_struct *files; + int new_fd; + + if (!access_ok(VERIFY_WRITE, datavp, sizeof(int))) + return -EFAULT; + + mm = get_task_mm(child); + files = get_files_struct(child); + vma = find_vma(mm, addr); + + if (!vma || vma->vm_start > addr) { + ret = -EFAULT; + goto err; + } + + if (!vma->vm_file || !files) { + ret = -EBADFD; + goto err; + } + + if (security_file_receive(vma->vm_file) != 0) { + ret = -EPERM; + goto err; + } + + new_fd = get_unused_fd_flags(O_CLOEXEC); + ret = __put_user(new_fd, datalp); + get_file(vma->vm_file); + fd_install(new_fd, vma->vm_file); + +err: + put_files_struct(files); + mmput(mm); + + return ret; +} + int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data) { @@ -1003,6 +1051,9 @@ int ptrace_request(struct task_struct *child, long request, break; } #endif + case PTRACE_GETMAPFD: + ret = ptrace_getmapfd (child, addr, data); + break; default: break; } -- 2.50.1