Thursday, January 17, 2019

Taking a page from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's book: A TLB issue in mremap()

Posted by Jann Horn, Project Zero

This is a technical blog post about TLB flushing bugs in kernels, intended for people interested in kernel security and memory management.

Introduction: Bugs in Memory Management code

There have been some pretty scary bugs in memory management in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 past, like:

  • CVE-2016-5195, a logic bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel that permitted writing to shared read-only pages
  • CVE-2018-1038, a Windows bug that existed for about two months, where a bit was set incorrectly in a page table, permitting userspace to overwrite page tables

Memory management is one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core functions that every kernel and hypervisor needs to implement; and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correctness of memory management code is very important to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire system. I hope that this post encourages more researchers to look at memory management code and demonstrates that memory management code can have issues with high security impact that fall somewhat outside of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 typical security bug patterns.

This blog post focuses on memory management bugs related to TLB flushing. Such bugs can, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 timing works out for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker, provide very strong exploitation primitives for local attacks; and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are hard to discover unless you are manually looking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. They are probably not a big bug class, but occasionally, bugs in TLB flushing logic do happen.

Here are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs related to TLB flushing that I have (co-)discovered:

  • Xen PV: XSA-241: "Stale TLB entry due to page type release race" (CVE-2017-15588) (security impact discovered by Xen security team)
  • Linux: insufficient shootdown for paging-structure caches (link)
  • gVisor: pagetable reuse across levels without paging-structure invalidation (link)
  • [XNU: pmap_flush() omits TLB flushes on machines with >32 logical CPU cores (link) - this was already fixed in a binary release when I reported it, so it doesn't really count]
  • Linux: mremap() TLB flush too late with concurrent ftruncate() (link) (CVE-2018-18281)

This blog post focuses on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list.

By cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way: Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gVisor bug is in memory management code written in Go, which is memory-safe-ish. This demonstrates that in operating system code, "logic bugs" in some places, like page table management, can have consequences that are as severe as those of classical memory safety issues, and are not in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scope of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 language's safety guarantees. Of course, memory-safe languages are still highly useful because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y (should) prevent bugs in random, non-critical pieces of kernel code from corrupting completely unrelated system state, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y allow reviewers to spend more time on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security-critical parts of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system.

Introduction: TLBs and paging-structure caches

If you know what a TLB is, what a TLB flush is, what paging-structure caches are, and how paging-structure caches are managed, you can skip this section. This section does not exhaustively describe cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 topic of TLB management; in particular, it doesn't deal with processor features like global page table entries and PCID/ASID.

Page tables contain information on how virtual addresses map to physical ones. Page tables are stored in memory, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are comparatively slow to access; to make address translation fast, CPUs use caches. The classic caches for this are called Translation Lookaside Buffers (TLBs); cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y cache mappings from virtual to physical page addresses (including mappings for huge pages), or in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y (more or less) cache last-level page table entries. (Modern CPU cores often have multiple TLBs with different responsibilities, e.g. Intel CPUs have an instruction TLB, a data TLB and a shared L2 TLB.) TLB parameters are usually fairly well-documented; for example:

  • Intel's Optimization Reference Manual has information about TLB structure in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "Cache and Memory Subsystem" subsections for various processor generations
  • Arm documents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB parameters of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir cores in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir processor documentation, in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Technical Reference Manual for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core, under "Memory Management Unit > TLB organization".
  • AMD publishes TLB parameters in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir Software Optimization Guides.

Paging-structure caches are usually less well-documented; but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is official documentation about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir existence and necessary precautions when dealing with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. Intel calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m "Paging-Structure Caches", Arm calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m "Intermediate table walk caches", AMD documents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m as part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 L2 data TLB (at least for 17h processors). Paging-structure caches store copies of non-last-level page table entries; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are used when a virtual address without a corresponding TLB entry is being accessed, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y reduce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of memory accesses for a page table walk. There are some reverse-engineered details about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 paging-structure caches of various processors in a VUSec paper (in Table 1).

It generally has to be assumed that entries in TLBs and paging-structure caches can be evicted by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processor whenever it wants to. Similarly, it has to be assumed that a processor can create entries in TLBs and paging-structure caches from page table entries whenever it wants to, because memory accesses in speculatively executed code can create such entries.

Mechanisms to invalidate TLB entries and paging-structure caches differ between processor architectures:

X86 provides instructions to invalidate eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r individual TLB entries for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current logical CPU core, or to invalidate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire TLB (eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r with or without global entries) for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current logical CPU core. Invalidating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB entry for a virtual address also at least implies invalidation of any paging-structure cache entries that could be used for translating that virtual address. The Intel SDM documents this in volume 3A, chapter 4.10.4 ("Invalidation of TLBs and Paging-Structure Caches"). (The SDM says that INVLPG invalidates all paging-structure caches, but doesn't make such broad guarantees for individual-address INVPCID as far as I can tell.) To perform TLB invalidation across logical CPU cores, an operating system has to manually run code that invalidates TLB entries on each logical CPU core; this is normally implemented by sending Inter-Processor Interrupts (via APIC) from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processor that wants to perform a TLB invalidation to all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processors that might have relevant stale TLB or paging-structure cache entries.

The ARM architecture provides magic instructions that can perform cross-core TLB invalidation for you; however, if you also need to synchronize against page table walks implemented in software (like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel), you may have to send IPIs anyway (depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 synchronization mechanism used for page table walks).

The general code pattern for performing cache invalidations for page table entries is:

  1. Remove an entry from a page table, but keep holding a reference to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical page it points to.
  2. Perform a TLB flush (eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r for a specific address, or for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 entire address space) across all cores that might be using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same page tables as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current thread.
  3. Drop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference that was held on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical page, potentially freeing it.

This pattern is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same both when unmapping normal data pages and when removing page tables. It can often be batched for better performance - first remove multiple page table entries, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n do one TLB flush across cores, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n drop all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page references -, but for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapping of an individual page (including page tables), this pattern is generally true.

On X86 (but ARM64 is similar), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are two bits in a last-level PTE which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU can write into as part of address translation: The Accessed bit specifies whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU has ever used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table entry for address translation; in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Accessed bit is unset, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table entry has not been cached by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table entry was written by software. The Dirty bit specifies whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU has ever used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table entry for a writing memory access; in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Dirty bit is unset, no TLB entries that can be used to write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical page have been created since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last software write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PTE.

Linux: mremap() TLB flush too late

The bug

On Linux, memory management data structures of a process are protected by multiple locks; in particular, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read/write semaphore mmap_sem in struct mm_struct is used to protect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VMA (virtual memory area) structures, and page table locks (if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is configured normally, implemented using per-page-table spinlocks for lower-level page tables) are used to protect access to page tables. Accesses to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page tables of a process for syscalls such as mmap()/mremap()/munmap(), as well as syscalls for page fault handling, use both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap_sem and page table locks. However, some ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r types of page table access (e.g. operations on all places across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system where a given file is mapped, like an ftruncate() syscall that shrinks a file and frees pages beyond cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file) don't hold cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap_sem and only use page table locks.

The mremap() syscall allows userspace to move a VMA and its associated page table entries. This syscall moves page tables via mremap_to() -> move_vma() -> move_page_tables() -> move_ptes(). The  move_ptes() function implemented roughly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following logic for moving entries between two L1 page tables, with only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap_sem held initially (locked in exclusive mode):

  1. (Take reverse map locks in some cases if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new VMA has been merged into an adjacent VMA.)
  2. Take page table locks on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old and new page tables.
  3. (Do a TLB flush if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 direct reclaim path is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle of stealing some pages from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process.)
  4. For each non-empty entry in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant range of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current source page table:
    1. Atomically read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table entry and clear it (using ptep_get_and_clear(), which e.g. on X86 boils down to a LOCK XCHG).
    2. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read page table entry is Dirty, set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local force_flush flag to true.
    3. Write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read page table entry into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new mapping.
  5. Unlock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new page table.
  6. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 force_flush flag was set, perform a TLB flush on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old page table entries that were accessed in step 4.
  7. Unlock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old page table.
  8. (Drop reverse map locks if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y were taken.)
  9. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 force_flush flag wasn't set, signal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller move_page_tables() that a TLB flush is required.

Later, after iterating over multiple page tables, move_page_tables() cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n performs a TLB flush on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old address range if requested.

move_ptes() needs to ensure that, when it releases cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old page table's reference, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re can be no more stale TLB entries. There is nothing in move_ptes() that explicitly drops a reference, but move_ptes() moves cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new page table entry. While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table locks on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new page table are held, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r tasks running concurrently can't yet remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new page table entry and drop its reference, so things are still fine after step 4c - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page can't be freed. But after step 5, anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365oretically race with mremap() and drop cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page. This is long before move_page_tables() performs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant TLB flush on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old address range (this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug I reported), and also slightly before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB flush is performed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 force_flush case (I didn't notice that, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel security team did).

On modern kernels, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 big race window only works for non-Dirty page table entries - in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 big race window can only be used for use-after-free reads, not use-after-free writes. However, before commit 5d1904204c99 (from November 2016, first in v4.9), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 special case for Dirty page table entries did not exist, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 big race window was also usable for use-after-free writes.

Almost everyone is using kernel versions >=4.9 nowadays - for example, Debian stable ships a kernel based on 4.9. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are some exceptions: RHEL still ships 3.10-based kernels, and many Android devices are based on kernels older than 4.9. For example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel branches used by Google's Pixel phones are:

  • Google Pixel: 3.18
  • Google Pixel 2: 4.4
  • Google Pixel 3: 4.9

I decided to write an exploit for Google's Pixel 2.

Locks and preemption

This section, along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following one, describes some background that will be useful for developing an exploit strategy.

The Linux kernel supports three different models for preemption of kernel code, one of which has to be selected at build time:

  • CONFIG_PREEMPT_NONE ("No Forced Preemption (Server)")
  • CONFIG_PREEMPT_VOLUNTARY ("Voluntary Kernel Preemption (Desktop)")
  • CONFIG_PREEMPT ("Preemptible Kernel (Low-Latency Desktop)")

(More preemption types are coming with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 realtime patchset, but that hasn't landed yet.)

The preemption model determines what happens when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel wishes to interrupt a task that is currently running kernel code - e.g. because a task with higher priority has become runnable and is waiting to be scheduled.

The Pixel 2 uses a kernel configured with CONFIG_PREEMPT. This means that by default, kernel code can be interrupted at any point during its execution. This even works while a task is holding a mutex, while it is holding a semaphore, or while it is in an RCU read-side critical section (depending on kernel configuration). Only something like a spinlock actually suppresses preemption.

As an attacker, we would like to make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race window between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time move_ptes() drops cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table lock and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB flush occurs in move_page_tables() as big as possible. Here, it is very useful for us that kernel code is preemptible: Because only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap_sem is held across cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race window, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mmap_sem does not inhibit preemption, we can potentially convince cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler to kick cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU core while it is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race window, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n keep cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task off cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU for an amount of time on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 order of milliseconds.

The kernel allows us to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 affinity of our tasks (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of CPU cores on which a task is allowed to run), and it also allows us to set various scheduler parameters that control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relative priority of our tasks. This means that we can use affinity masks to pin multiple processes we own togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r onto a single CPU core, with different priorities - meaning that waking up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 higher-priority task implies preemption of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lower-priority one. In this case, by assigning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SCHED_IDLE priority to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task running mremap(), pinning it togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r with a task that has normal priority and is blocking on a read() from a pipe, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n writing to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r side of that pipe in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right moment, we can preempt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mremap() syscall.

To know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right moment for calling write() on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe, we can abuse procfs. The procfs file /proc//status contains various fields about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory use of a process, including cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VmPTE field, which shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 amount of memory consumed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page tables of a process. By busy-polling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 status file and monitoring cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VmPTE field, it is possible to detect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page table allocations performed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mremap() syscall.

The page allocator

The Linux page allocator is based on a buddy allocator, implemented in mm/page_alloc.c. This allocator tracks free pages of different orders; an order-n page is 212+n bytes big and is aligned to a 212+n-byte boundary (assuming that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system is using a native page size of 212 bytes).

Page freelists are not just per-order, but also per-zone, per-migration-type and (on NUMA systems, which isn't relevant for Android phones) per-node.

The zone specifies in which ways a page can be used; pages stay associated with a single zone. The following zones can exist; bold text indicates that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zone actually exists on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Pixel 2:

  • ZONE_DMA: like ZONE_NORMAL, but can also be used for DMA with devices that can only address a small subset of physical memory (used by arm64 before kernel 4.16)
  • ZONE_DMA32: like ZONE_NORMAL, but can also be used for DMA with devices that can only use 32-bit physical addresses (used by arm64 since kernel 4.16)
  • ZONE_NORMAL: can be used for normal kernel memory allocations and as userspace memory; page is mapped in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 linear mapping
  • ZONE_HIGHMEM: Can only be used for special types of kernel memory allocations and as userspace memory; page is not mapped in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 linear mapping. This doesn't exist on arm64, since virtual memory is large enough to map all physical memory.
  • ZONE_MOVABLE: manually reserved for pages that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can (usually) move to a different physical address when needed (basically, userspace memory); this enables limited memory hotplugging and reduces fragmentation (which can help with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocation of hugepages); cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Pixel 2 doesn't seem to be using this
  • ZONE_DEVICE: something about persistent memory? - arm64 never uses this


The migration type of a page specifies eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r what kind of allocation cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page is currently being used for (if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page is currently in use) or what kind of allocation cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page should preferably be used for (if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page is free); cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intent is to cluster pages that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can reclaim by moving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir contents togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to later create high-order free pages by moving data out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way. The following migration types exist:

  • MIGRATE_UNMOVABLE: for allocations that can't simply be removed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir physical page whenever cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel wants to have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page for something else - e.g. normal kmalloc() allocations
  • MIGRATE_MOVABLE: for data that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can (usually) simply move to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r physical page - e.g. userspace memory
  • MIGRATE_RECLAIMABLE: for allocations that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can't simply move to a different address, but that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel can free if necessary to free up some memory
  • MIGRATE_CMA: special memory reserves for contiguous memory for DMA, can only be used for specific DMA allocations and for movable allocations
  • MIGRATE_ISOLATE: no allocations are possible - used for purposes like memory hot-removal and blacklisting of defective RAM at runtime

The first two or three of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most relevant ones - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest are kinda special.

The page allocator also has per-cpu, per-zone, per-migratetype freelists as a performance optimization. These only contain order-0 pages. In kernel versions <4.15, one annoying thing about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-cpu freelists is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can be accessed from both sides. Normal freelist accesses push and pop on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same end so that pages coming from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freelist are more likely to be in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU cache; but when freeing pages that are expected to be cache-cold, and when allocating pages that have to wait for DMA before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are written to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first time, old kernel versions access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freelist from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r end.

The algorithm for allocating pages via get_page_from_freelist(), before entering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 slowpath, works roughly as follows (ignoring things like NUMA and atomic/realtime allocations):

  • For each zone (from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most preferred zone to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least preferred zone); in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Pixel 2, when allocating non-DMA memory, first for ZONE_NORMAL, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n for ZONE_DMA:
    • rmqueue_pcplist(): If we want an order-0 page, attempt to allocate from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-cpu freelist for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current zone and our preferred migratetype. If this freelist is empty, try to refill it by looking through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-order freelists for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current zone and our preferred migratetype, starting at order 0, iterating through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freelists with increasing order (standard buddy allocator behavior).
    • Attempt to allocate from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buddy allocator directly, by iterating through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-order freelists for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current zone and our preferred migratetype with increasing order.
    • If we want a movable page, attempt to allocate from MIGRATE_CMA memory instead.
    • __rmqueue_fallback(): Tries to grab a free block of maximum order from a freelist with a different migration type, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n potentially changes that block's migration type to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 desired one.

For an attacker attempting to exploit a use-after-free at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page allocator level, this means that getting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to reallocate a movable page for an unmovable allocation, or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r way around, requires creating memory pressure that forces cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buddy allocator to go through __rmqueue_fallback() and steal pages from a different migration type.

Exploit strategy

For exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TLB invalidation race, we want to quickly reallocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed movable page from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache. Preferably we'll do this through a per-cpu freelist, so it is probably easier to have it reallocated as a movable page instead of forcing a migratetype change. With this strategy, we can't attack things like normal kernel memory allocations or page tables, but we can attack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache and anonymous userspace memory. I chose to poison page cache memory, since I wanted to avoid having ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r userspace processes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 critical timing path of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack.

This means that at a high level, to perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack, we need to pick a victim file page (in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, a page-aligned and page-sized area in a file) that we want to corrupt, in a file to which we have read-only access (e.g. a shared library containing executable code). Then, we need to poison cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache entry for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim file page by running roughly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following steps in a loop:

  1. Somehow evict cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim file page from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache.
  2. Allocate a set of file-backed pages (e.g. by writing to a memfd), and map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m as mapping A.
  3. Trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mremap/ftruncate race to free cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file-backed pages without removing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding TLB entries for mapping A.
  4. Start a read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page, causing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to reallocate one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed pages as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache entry for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page.
  5. Poll cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of pages in mapping A (through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stale TLB entries) until one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page. If a page fault occurs before that, go back to step 1.
  6. At this point, we have a stale TLB entry translating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old mapping A to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page. Therefore, we can now repeatedly overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page through mapping A. (In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory, it seems like a single overwrite should be sufficient; but in practice, that doesn't seem to work. I'm not sure whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this is caused by some sort of cache inconsistency (because memory is concurrently written via DMA and by software), or whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r I did something else wrong.)


On kernels <4.15, because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 annoying two-sided behavior of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-cpu freelist, when a new physical page is allocated to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page, it comes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "cold" end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-cpu freelist; so instead of simply pushing a page with a stale TLB entry onto cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-cpu freelist and letting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel use it for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page, it is necessary to quickly push enough pages with stale TLB entries to force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to move all existing per-cpu freelist entries to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global freelist.

Forcing page cache reloads

This section focuses on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first step of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit strategy, evicting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache.

Public prior research on this topic that I used for my PoC is https://arxiv.org/abs/1710.00551 ("Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r Flip in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Wall of Rowhammer Defenses"), which uses page cache eviction as a mechanism to repeatedly move file-backed pages to different physical pages. This paper says in section VIII-B:

A fundamental observation we made is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 replacement algorithm of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux page cache prioritizes eviction of nonexecutable pages over executable pages.

In shrink_active_list() and page_check_references() in mm/vmscan.c, you can see that file-backed executable pages indeed get special handling:

static void shrink_active_list(unsigned long nr_to_scan,
                  struct lruvec *lruvec,
                  struct scan_control *sc,
                  enum lru_list lru)
{
[...]
   /*
    * Identify referenced, file-backed active pages and
    * give cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m one more trip around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 active list. So
    * that executable code get better chances to stay in
    * memory under moderate memory pressure.  Anon pages
    * are not likely to be evicted by use-once streaming
    * IO, plus JVM can create lots of anon VM_EXEC pages,
    * so we ignore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m here.
    */
   if ((vm_flags & VM_EXEC) && page_is_file_cache(page)) {
       list_add(&page->lru, &l_active);
       continue;
   }
[...]
}
[...]
static enum page_references page_check_references(struct page *page,
                         struct scan_control *sc)
{
[...]
   /*
    * Activate file-backed executable pages after first usage.
    */
   if (vm_flags & VM_EXEC)
       return PAGEREF_ACTIVATE;

   return PAGEREF_KEEP;
[...]
}

Therefore, executable file-backed pages are used to create memory pressure to evict cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page.

For this attack, it is also desirable that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page, once evicted, is not reloaded from disk until it is accessed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next time. This is not always cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case: The kernel has some readahead logic that, depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 observed memory access pattern, may read large amounts of data (up to VM_MAX_READAHEAD, which is 128KiB) around a page fault from disk. This is implemented in filemap_fault() by calling into do_async_mmap_readahead() / do_sync_mmap_readahead(). An attacking process can simply opt out of this for its own accesses, but it is also desirable to suppress this behavior for accesses coming from ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes that might be executing code from ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pages in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim file.

For this reason, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoC first evicts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n accesses all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pages in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim file through a mapping with MADV_RANDOM to reduce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 probability that accesses to those ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r pages trigger readahead logic: When a page being accessed is present in RAM, synchronous readahead won't happen; and when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page being accessed with a minor fault (i.e. cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page is present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache, but no corresponding page table entry exists yet) is not marked as PG_readahead, asynchronous readahead won't happen eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r.

Picking a victim page

My exploit targets a victim page in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 library /system/lib64/libandroid_runtime.so that contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function com_android_internal_os_Zygote_nativeForkAndSpecialize(). This function is executed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zygote process whenever an app process needs to be launched — in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, it shouldn't run very often on an idle device, meaning that we can evict it and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n have time to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug —, and we can trigger its execution by launching an isolated service, so we can easily cause its execution immediately after successfully triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug. The zygote process has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CAP_SYS_ADMIN capability (and is permitted to use it), and because its job is to fork off children that become app processes and system_server, it has access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contexts of system_server and every app.

To demonstrate that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code injection into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zygote is working, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 injected code reads its own SELinux context and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n overwrites cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hostname with that string (using sethostname()).

Putting it togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r

The exploit is packaged in an app that, when you press cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "run" button, first uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code in eviction.c to flush cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page in /system/lib64/libandroid_runtime.so from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page cache; afterwards, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code in sched_test.c is used to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mremap bug and overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page. If sched_test.c reports that it has successfully located and overwritten cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 targeted code page, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Java code launches cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 isolated app TriggerService to trigger execution of com_android_internal_os_Zygote_nativeForkAndSpecialize(); ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack is restarted.

sched_test.c executes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following threads:
  • idle_worker(): on core 4, with SCHED_IDLE priority; is moved to core 3 during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack
  • spinner(): on core 4, with normal priority
  • nicer_spinner(): on core 3, with normal priority
  • read_worker(): on core 5, with normal priority
  • main(): on core 6, with normal priority

The following screenshot shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 running exploit, which has performed a few exploit attempts already, but hasn't managed to visibly trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug yet:


In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next screenshot, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit has managed to read data through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stale TLB entry, but still hasn't managed to locate and overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim page:


In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 third screenshot, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit has succeeded:

Timeline

This bug was reported to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel on 2018-10-12.
A fix was committed and made public six days later, on 2018-10-18.
Two days after that, on 2018-10-20, new upstream stable kernels were released on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 branches 4.9, 4.14 and 4.18.
On 2018-10-29, we published cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug report.
On 2018-11-10, an upstream backport on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 4.4 branch was released.
On 2018-11-14, we published cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit described in this blogpost.

It took more than two months for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upstream kernel change to make its way to user devices; writing an exploit for this bug took far less time.

Conclusion

There isn't really an overarching conclusion here, but some takeaways:

  • Bugs in TLB flushing logic can be exploitable and lead to system compromise from unprivileged userspace.
  • When trying to exploit a use-after-free of a physical page on Linux, keep in mind that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page allocator will try to avoid changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 migration types of pages, so usually movable pages (anonymous userspace memory and page cache) will be reused as movable pages, and unmovable pages (normal kernel memory) will be reused as unmovable pages.
  • Knowing a bit about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler, and in particular preemption, can be very helpful for widening kernel race windows. Linux exposes fairly powerful control over scheduling to unprivileged userspace.
  • Android takes months to ship an upstream kernel security fix to users; it would be nice if that was significantly faster.