]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
ptrace: Add PTRACE_GETMAPFD.
authorNick Alcock <nick.alcock@oracle.com>
Thu, 17 Jan 2013 14:25:40 +0000 (14:25 +0000)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 29 Jun 2015 21:41:42 +0000 (22:41 +0100)
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 <nick.alcock@oracle.com>
include/uapi/linux/ptrace.h
kernel/ptrace.c

index cf1019e15f5bc57c0fbf6120a96deb2340465294..d6aa5375eb5ae4657a9f61c687f6bdaebf1f30a7 100644 (file)
 #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
index c8e0e050a36afb0ccb875e13d9f2b526b0af4d29..5c511f403e42a1ff26e7d4217be7d956fa06eef0 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/ptrace.h>
@@ -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;
        }