Skip to content

Pipe buffer grooming

Linux pipe'ları ring'lerini boyutu fcntl(F_SETPIPE_SZ) üzerinden attacker tarafından seçilebilen, kmalloc'lanmış bir struct pipe_buffer array'iyle destekler ve bu, pipe'ları kernel heap feng shui için kesin, spray'lenebilir, reclaim edilebilir bir object kılar.

Mechanism

Bir pipe'ın data ring'i, kmalloc() ile allocate edilen bir struct pipe_buffer array'idir (alloc_pipe_info() / pipe_resize_ring() içinde). Her eleman küçük ve iyi yapılandırılmıştır:

struct pipe_buffer {
    struct page *page;                       // physical page backing this slot
    unsigned int offset, len;                // window into the page
    const struct pipe_buf_operations *ops;   // function-pointer table
    unsigned int flags;
    unsigned long private;
};

Note

Grooming gücü üç özellikten gelir. (1) Size kontrolü: pipe_buffer slot sayısı ve dolayısıyla arkadaki kmalloc allocation'ının boyutu, attacker tarafından fcntl(fd, F_SETPIPE_SZ, n) ile seçilir (round_pipe_size() tarafından power-of-two bir page sayısına yuvarlanır). Array'in hangi kmalloc-N cache'ine düşeceğini sen seçersin. (2) Toplu instantiation: birçok pipe açmak birçok aynı-boyutlu allocation spray'ler ve bir alt kümeyi seçerek close()'lamak öngörülebilir delikler açar — freed bir victim slot'unu kontrol edilebilir allocation'ların yanına yerleştirmek ya da aynı cache size'ından freed bir object'i reclaim etmek için klasik feng shui. (3) Yararlı içerik: ops ve page field'ları sulu corruption hedefleridir — ops'u overwrite etmek function pointer'ları yönlendirir (pipe_buf_operations hijack); kontrol edilen bir page pointer'ı sıradan read()/write() üzerinden arbitrary read/write verir.

Yani pipe'lar çifte görev yapar: bir grooming object'i (istediğin gibi spray'leyip/free edebileceğin öngörülebilir kmalloc allocation'ları) ve bir target object'i (field'ları bir heap overflow/UAF'i R/W'ye çeviren bir object).

Walkthrough

Temsili bir grooming dizisi (Alexander Popov'un pipe_buffer deneylerindeki gibi):

#define N 1024
int p[N][2];
for (int i = 0; i < N; i++) {
    pipe(p[i]);
    // shrink right after creation so we don't hit the
    // /proc/sys/fs/pipe-user-pages-soft limit (default 16384 pages)
    fcntl(p[i][1], F_SETPIPE_SZ, 2 * PAGE_SIZE); // -> small kmalloc cache
    // write a unique tag so we can recognize this pipe's buffer later
    write(p[i][1], &i, sizeof(i));
}

Sonra bir victim'in yanında delikler aç ve vulnerable allocation'ın freed bir slot'u yeniden kullanmasına izin ver:

// free every other pipe's buffer array to create gaps in the slab
for (int i = 0; i < N; i += 2) close(p[i][0]), close(p[i][1]);
// trigger the bug here: the vulnerable kmalloc lands in one of the holes,
// overlapping a surviving pipe_buffer array.
F_SETPIPE_SZ cache seçimi için neden önemli

Kritik nokta: pipe kapasitesi byte olarak değil, slot olarak sayılır — her pipe_buffer slot'u tam olarak bir page'i backler, yani F_SETPIPE_SZ ile istenen N-page'lik bir pipe = N adet slot demektir. Array'in kmalloc boyutu N * sizeof(struct pipe_buffer) (64-bit'te slot başına ~40 byte) olur, pipe'ın veri kapasitesi (N page) değil. Böylece resize, array'in hangi slab'ı kullandığını değiştirir; örn. 2-page'lik bir pipe = 2 slot ≈ 80 byte, düşük bir kmalloc cache'ine (kernel'e göre kmalloc-96) düşerken, varsayılan 64 KiB / 16-page pipe = 16 slot ≈ 640 byte, kmalloc-1k'ya düşer. Victim object'inin cache'ini tutturmak oyunun tamamıdır.

Warning

Per-user pipe page accounting (pipe-user-pages-soft/-hard) ne kadar spray'leyebileceğini sınırlar; her pipe'ı pipe()'tan hemen sonra küçültmek için F_SETPIPE_SZ çağırmak seni limitin altında tutar, böylece büyük spray'ler mümkün kalır.

Detection

  • Tek bir process'ten gelen olağandışı büyük pipe2() + fcntl(F_SETPIPE_SZ) patlamaları anomalidir.
  • KASAN/CONFIG_SLAB_FREELIST_HARDENED, grooming'in kurduğu corruption'ların çoğunu yakalar.

Mitigation

  • CONFIG_SLAB_FREELIST_RANDOM ve CONFIG_SLAB_FREELIST_HARDENED adjacency öngörülebilirliğini azaltır.
  • Dedike/segregated cache'ler (örn. kmalloc-cg, hardened usercopy) grooming'in dayandığı cross-object reuse'u sınırlar.

References