lkml.org 
[lkml]   [2020]   [Jul]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    SubjectRe: [PATCH v1 0/4] [RFC] Implement Trampoline File Descriptor
    From
    Date
    On 7/28/2020 6:10 AM, madvenka@linux.microsoft.com wrote:
    > From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>
    >
    > Introduction
    > ------------
    >
    > Trampolines are used in many different user applications. Trampoline
    > code is often generated at runtime. Trampoline code can also just be a
    > pre-defined sequence of machine instructions in a data buffer.
    >
    > Trampoline code is placed either in a data page or in a stack page. In
    > order to execute a trampoline, the page it resides in needs to be mapped
    > with execute permissions. Writable pages with execute permissions provide
    > an attack surface for hackers. Attackers can use this to inject malicious
    > code, modify existing code or do other harm.
    >
    > To mitigate this, LSMs such as SELinux may not allow pages to have both
    > write and execute permissions. This prevents trampolines from executing
    > and blocks applications that use trampolines. To allow genuine applications
    > to run, exceptions have to be made for them (by setting execmem, etc).
    > In this case, the attack surface is just the pages of such applications.
    >
    > An application that is not allowed to have writable executable pages
    > may try to load trampoline code into a file and map the file with execute
    > permissions. In this case, the attack surface is just the buffer that
    > contains trampoline code. However, a successful exploit may provide the
    > hacker with means to load his own code in a file, map it and execute it.
    >
    > LSMs (such as the IPE proposal [1]) may allow only properly signed object
    > files to be mapped with execute permissions. This will prevent trampoline
    > files from being mapped. Again, exceptions have to be made for genuine
    > applications.
    >
    > We need a way to execute trampolines without making security exceptions
    > where possible and to reduce the attack surface even further.
    >
    > Examples of trampolines
    > -----------------------
    >
    > libffi (A Portable Foreign Function Interface Library):
    >
    > libffi allows a user to define functions with an arbitrary list of
    > arguments and return value through a feature called "Closures".
    > Closures use trampolines to jump to ABI handlers that handle calling
    > conventions and call a target function. libffi is used by a lot
    > of different applications. To name a few:
    >
    > - Python
    > - Java
    > - Javascript
    > - Ruby FFI
    > - Lisp
    > - Objective C
    >
    > GCC nested functions:
    >
    > GCC has traditionally used trampolines for implementing nested
    > functions. The trampoline is placed on the user stack. So, the stack
    > needs to be executable.
    >
    > Currently available solution
    > ----------------------------
    >
    > One solution that has been proposed to allow trampolines to be executed
    > without making security exceptions is Trampoline Emulation. See:
    >
    > https://pax.grsecurity.net/docs/emutramp.txt
    >
    > In this solution, the kernel recognizes certain sequences of instructions
    > as "well-known" trampolines. When such a trampoline is executed, a page
    > fault happens because the trampoline page does not have execute permission.
    > The kernel recognizes the trampoline and emulates it. Basically, the
    > kernel does the work of the trampoline on behalf of the application.

    What prevents a malicious process from using the "well-known" trampoline
    to its own purposes? I expect it is obvious, but I'm not seeing it. Old
    eyes, I suppose.

    > Here, the attack surface is the buffer that contains the trampoline.
    > The attack surface is narrower than before. A hacker may still be able to
    > modify what gets loaded in the registers or modify the target PC to point
    > to arbitrary locations.
    >
    > Currently, the emulated trampolines are the ones used in libffi and GCC
    > nested functions. To my knowledge, only X86 is supported at this time.
    >
    > As noted in emutramp.txt, this is not a generic solution. For every new
    > trampoline that needs to be supported, new instruction sequences need to
    > be recognized by the kernel and emulated. And this has to be done for
    > every architecture that needs to be supported.
    >
    > emutramp.txt notes the following:
    >
    > "... the real solution is not in emulation but by designing a kernel API
    > for runtime code generation and modifying userland to make use of it."
    >
    > Trampoline File Descriptor (trampfd)
    > --------------------------
    >
    > I am proposing a kernel API using anonymous file descriptors that
    > can be used to create and execute trampolines with the help of the
    > kernel. In this solution also, the kernel does the work of the trampoline.
    > The API is described in patch 1/4 of this patchset. I provide a
    > summary here:
    >
    > Trampolines commonly execute the following sequence:
    >
    > - Load some values in some registers and/or
    > - Push some values on the stack
    > - Jump to a target PC
    >
    > libffi and GCC nested function trampolines fit into this model.
    >
    > Using the kernel API, applications and libraries can:
    >
    > - Create a trampoline object
    > - Associate a register context with the trampoline (including
    > a target PC)
    > - Associate a stack context with the trampoline
    > - Map the trampoline into a process address space
    > - Execute the trampoline by executing at the trampoline address
    >
    > The kernel creates the trampoline mapping without any permissions. When
    > the trampoline is executed by user code, a page fault happens and the
    > kernel gets control. The kernel recognizes that this is a trampoline
    > invocation. It sets up the user registers based on the specified
    > register context, and/or pushes values on the user stack based on the
    > specified stack context, and sets the user PC to the requested target
    > PC. When the kernel returns, execution continues at the target PC.
    > So, the kernel does the work of the trampoline on behalf of the
    > application.
    >
    > In this case, the attack surface is the context buffer. A hacker may
    > attack an application with a vulnerability and may be able to modify the
    > context buffer. So, when the register or stack context is set for
    > a trampoline, the values may have been tampered with. From an attack
    > surface perspective, this is similar to Trampoline Emulation. But
    > with trampfd, user code can retrieve a trampoline's context from the
    > kernel and add defensive checks to see if the context has been
    > tampered with.
    >
    > As for the target PC, trampfd implements a measure called the
    > "Allowed PCs" context (see Advantages) to prevent a hacker from making
    > the target PC point to arbitrary locations. So, the attack surface is
    > narrower than Trampoline Emulation.
    >
    > Advantages of the Trampoline File Descriptor approach
    > -----------------------------------------------------
    >
    > - trampfd is customizable. The user can specify any combination of
    > allowed register name-value pairs in the register context and the kernel
    > will set it up accordingly. This allows different user trampolines to be
    > converted to use trampfd.
    >
    > - trampfd allows a stack context to be set up so that trampolines that
    > need to push values on the user stack can do that.
    >
    > - The initial work is targeted for X86 and ARM. But the implementation
    > leverages small portions of existing signal delivery code. Specifically,
    > it uses pt_regs for setting up user registers and copy_to_user()
    > to push values on the stack. So, this can be very easily ported to other
    > architectures.
    >
    > - trampfd provides a basic framework. In the future, new trampoline types
    > can be implemented, new contexts can be defined, and additional rules
    > can be implemented for security purposes.
    >
    > - For instance, trampfd defines an "Allowed PCs" context in this initial
    > work. As an example, libffi can create a read-only array of all ABI
    > handlers for an architecture at build time. This array can be used to
    > set the list of allowed PCs for a trampoline. This will mean that a hacker
    > cannot hack the PC part of the register context and make it point to
    > arbitrary locations.
    >
    > - An SELinux setting called "exectramp" can be implemented along the
    > lines of "execmem", "execstack" and "execheap" to selectively allow the
    > use of trampolines on a per application basis.
    >
    > - User code can add defensive checks in the code before invoking a
    > trampoline to make sure that a hacker has not modified the context data.
    > It can do this by getting the trampoline context from the kernel and
    > double checking it.
    >
    > - In the future, if the kernel can be enhanced to use a safe code
    > generation component, that code can be placed in the trampoline mapping
    > pages. Then, the trampoline invocation does not have to incur a trip
    > into the kernel.
    >
    > - Also, if the kernel can be enhanced to use a safe code generation
    > component, other forms of dynamic code such as JIT code can be
    > addressed by the trampfd framework.
    >
    > - Trampolines can be shared across processes which can give rise to
    > interesting uses in the future.
    >
    > - Trampfd can be used for other purposes to extend the kernel's
    > functionality.
    >
    > libffi
    > ------
    >
    > I have implemented my solution for libffi and provided the changes for
    > X86 and ARM, 32-bit and 64-bit. Here is the reference patch:
    >
    > http://linux.microsoft.com/~madvenka/libffi/libffi.txt
    >
    > If the trampfd patchset gets accepted, I will send the libffi changes
    > to the maintainers for a review. BTW, I have also successfully executed
    > the libffi self tests.
    >
    > Work that is pending
    > --------------------
    >
    > - I am working on implementing an SELinux setting called "exectramp"
    > similar to "execmem" to allow the use of trampfd on a per application
    > basis.

    You could make a separate LSM to do these checks instead of limiting
    it to SELinux. Your use case, your call, of course.

    >
    > - I have a comprehensive test program to test the kernel API. I am
    > working on adding it to selftests.
    >
    > References
    > ----------
    >
    > [1] https://microsoft.github.io/ipe/
    > ---
    > Madhavan T. Venkataraman (4):
    > fs/trampfd: Implement the trampoline file descriptor API
    > x86/trampfd: Support for the trampoline file descriptor
    > arm64/trampfd: Support for the trampoline file descriptor
    > arm/trampfd: Support for the trampoline file descriptor
    >
    > arch/arm/include/uapi/asm/ptrace.h | 20 ++
    > arch/arm/kernel/Makefile | 1 +
    > arch/arm/kernel/trampfd.c | 214 +++++++++++++++++
    > arch/arm/mm/fault.c | 12 +-
    > arch/arm/tools/syscall.tbl | 1 +
    > arch/arm64/include/asm/ptrace.h | 9 +
    > arch/arm64/include/asm/unistd.h | 2 +-
    > arch/arm64/include/asm/unistd32.h | 2 +
    > arch/arm64/include/uapi/asm/ptrace.h | 57 +++++
    > arch/arm64/kernel/Makefile | 2 +
    > arch/arm64/kernel/trampfd.c | 278 ++++++++++++++++++++++
    > arch/arm64/mm/fault.c | 15 +-
    > arch/x86/entry/syscalls/syscall_32.tbl | 1 +
    > arch/x86/entry/syscalls/syscall_64.tbl | 1 +
    > arch/x86/include/uapi/asm/ptrace.h | 38 +++
    > arch/x86/kernel/Makefile | 2 +
    > arch/x86/kernel/trampfd.c | 313 +++++++++++++++++++++++++
    > arch/x86/mm/fault.c | 11 +
    > fs/Makefile | 1 +
    > fs/trampfd/Makefile | 6 +
    > fs/trampfd/trampfd_data.c | 43 ++++
    > fs/trampfd/trampfd_fops.c | 131 +++++++++++
    > fs/trampfd/trampfd_map.c | 78 ++++++
    > fs/trampfd/trampfd_pcs.c | 95 ++++++++
    > fs/trampfd/trampfd_regs.c | 137 +++++++++++
    > fs/trampfd/trampfd_stack.c | 131 +++++++++++
    > fs/trampfd/trampfd_stubs.c | 41 ++++
    > fs/trampfd/trampfd_syscall.c | 92 ++++++++
    > include/linux/syscalls.h | 3 +
    > include/linux/trampfd.h | 82 +++++++
    > include/uapi/asm-generic/unistd.h | 4 +-
    > include/uapi/linux/trampfd.h | 171 ++++++++++++++
    > init/Kconfig | 8 +
    > kernel/sys_ni.c | 3 +
    > 34 files changed, 1998 insertions(+), 7 deletions(-)
    > create mode 100644 arch/arm/kernel/trampfd.c
    > create mode 100644 arch/arm64/kernel/trampfd.c
    > create mode 100644 arch/x86/kernel/trampfd.c
    > create mode 100644 fs/trampfd/Makefile
    > create mode 100644 fs/trampfd/trampfd_data.c
    > create mode 100644 fs/trampfd/trampfd_fops.c
    > create mode 100644 fs/trampfd/trampfd_map.c
    > create mode 100644 fs/trampfd/trampfd_pcs.c
    > create mode 100644 fs/trampfd/trampfd_regs.c
    > create mode 100644 fs/trampfd/trampfd_stack.c
    > create mode 100644 fs/trampfd/trampfd_stubs.c
    > create mode 100644 fs/trampfd/trampfd_syscall.c
    > create mode 100644 include/linux/trampfd.h
    > create mode 100644 include/uapi/linux/trampfd.h
    >

    \
     
     \ /
      Last update: 2020-07-28 18:06    [W:4.284 / U:0.152 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site