TL;DR

Alexander Popov (@a13xp0p0v) vừa xuất bản bài viết "Some notes on the security properties of the pipe_buffer kernel object" ngày 20/04/2026, tổng hợp các thí nghiệm trong dự án kernel-hack-drill của ông. Bài viết giải mã vì sao struct pipe_buffer — chỉ 40 byte — lại là mục tiêu heap-spray số một của giới exploit Linux kernel hiện đại, và hé lộ một chi tiết trong anon_pipe_get_page() khiến primitive AARW trở nên tự khuếch đại.

UNLIMITED PIPE_BUFFER EXPERIMENTS — hero image from Alexander Popov's blog

Có gì mới

Bài phân tích không công bố CVE, mà là một tài liệu kỹ thuật dày đặc — Popov gọi là "notes" — ghi lại những gì ông học được khi build exploit cho CVE-2024-50264 (lỗ hổng mptcp) bằng harness kernel-hack-drill. Ba đóng góp đáng chú ý:

  • Liệt kê đầy đủ 4 primitive có thể dựng từ pipe_buffer: Dirty Pipe (CVE-2022-0847), control-flow hijack qua ops, arbitrary kernel read/write qua page+offset+len, và PageJack — chỉ cần partial overwrite byte thấp của con trỏ page là đã redirect được sang page của pipe khác.
  • Giải thích tại sao anon_pipe_get_page() cache page trong pipe_inode_info.tmp_page[2], khiến pipe_buffer thứ hai tự động kế thừa con trỏ bị corrupt — bạn corrupt 1 lần, hưởng nhiều lần.
  • Đưa ra trick thực dụng: gọi daemon() để giữ pipe mở, tránh việc đóng pipe trả page bị corrupt lại cho buddy allocator → panic ngẫu nhiên.

Vì sao quan trọng

Từ Dirty Pipe 2022 tới giờ, pipe_buffer đã trở thành "universal target" cho exploit Linux kernel. Nhưng tài liệu công khai gộp đủ 4 primitive + các chi tiết slab spray + quota tuning + tính ổn định post-exploit thì rất hiếm. Bài của Popov chính là tài liệu field manual đó. Với defender, nó là checklist để kiểm tra hardening (CONFIG_BUG_ON_DATA_CORRUPTION, SLAB_FREELIST_HARDENED, RANDOM_KMALLOC_CACHES). Với researcher, nó là blueprint cho các CVE tiếp theo chạm vào slab kmalloc-1k.

Fact kỹ thuật

Layout struct pipe_buffer trên kernel v6.18:

struct pipe_buffer {
    struct page *page;                     /* 0   8 */
    unsigned int offset;                   /* 8   4 */
    unsigned int len;                      /* 12  4 */
    const struct pipe_buf_operations *ops; /* 16  8 */
    unsigned int flags;                    /* 24  4 */
    long unsigned int private;             /* 32  8 */
}; /* size 40, cachelines 1 */
Thông sốGiá trị mặc địnhÝ nghĩa cho attacker
Pipe capacity65,536 byteCấp 16 pipe_buffer = 640 byte/pipe
Slab cache landingkmalloc-1kÍt nhiễu, dễ spray
pipe-user-pages-soft16,384 pageVượt limit → pipe sau bị co lại, fragment slab
pipe-max-size1,048,576 byte (256 page)Có thể resize để rơi vào slab khác
Min capacity4,096 byte (1 page)Floor khi resize

Toán spray: 1024 pipe × 16 buffer = 16,384 page — vừa đúng ceiling soft limit. Nắm con số này thì tune exploit ổn định hơn nhiều.

So sánh 4 primitive

PrimitiveField corruptKết quảVí dụ CVE
Dirty PipeflagsGhi được file read-onlyCVE-2022-0847
Control-flow hijackops (function table)RIP control → kROPLớp kernel RCE cổ điển
AARWpage + offset + lenĐọc/ghi tuỳ ý kernel memoryDùng trong chuỗi CVE-2024-50264
PageJackPartial page (byte thấp)Trỏ sang page của pipe khác → page UAFPrimitive mới Popov nhấn mạnh

Use case: AARW hiệu quả

Popov mô tả quy trình chuẩn:

  1. Resize pipe để rơi vào slab cache mục tiêu.
  2. Ghi full page vào pipe.
  3. Corrupt con trỏ pipe_buffer.page đầu tiên.
  4. read() → leak page kernel về userspace.
  5. Sửa nội dung, write() → đẩy ngược vào kernel memory.

Điểm lợi: vì anon_pipe_get_page() cache page đã release trong tmp_page[2], pipe_buffer thứ hai sẽ nhận lại đúng con trỏ bị corrupt khi write() quay lại pipe. Muốn quét nhiều page khác nhau thì cycle qua đủ 16 pipe_buffer giữa các lần ghi, tránh giá trị cache ghi đè target mới.

Giới hạn & mitigation

  • pipe-user-pages-hard chặn spray vô hạn từ unprivileged user.
  • Bảng pipe_buf_operations nằm trong read-only kernel data — control-flow hijack vẫn cần kASLR leak trước.
  • Hardening hiện đại (CONFIG_BUG_ON_DATA_CORRUPTION, SLAB_FREELIST_HARDENED, RANDOM_KMALLOC_CACHES) làm spray ồn hơn nhưng không vô hiệu hoá primitive.
  • Đóng pipe có page corrupt → trả page kernel vào buddy allocator → panic. Fix: daemon() giữ tiến trình sống, pipe mở.
  • Bài viết không phải disclosure — không có patch mới, các fix Dirty Pipe (5.16.11 / 5.15.25) vẫn là mốc phòng thủ cũ.

Tiếp theo

Popov bóng gió về một hướng mới: page-reclamation attack — lạm dụng hành vi buddy allocator khi pipe page được giải phóng có kiểm soát. kernel-hack-drill vẫn là sandbox chính để ông thử nghiệm, nên kỳ vọng các bài tiếp theo sẽ đào sâu vector này.

Nguồn: a13xp0p0v.github.io, kernel-hack-drill & CVE-2024-50264, xairy/linux-kernel-exploitation.