<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>BeaCox&#39;s Blog</title>
    <link>https://blog.beacox.space/tags/slub/</link>
    <description>Stay humble, remain critical.</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <managingEditor>root@beacox.space (BeaCox)</managingEditor>
    <webMaster>root@beacox.space (BeaCox)</webMaster>
    <copyright>© Since 2022 BeaCox</copyright>
    <lastBuildDate>Fri, 09 Oct 2026 08:04:33 +0000</lastBuildDate><atom:link href="https://blog.beacox.space/tags/slub/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从 Cache of Castaways 学 cross-cache</title>
      <link>https://blog.beacox.space/posts/cross-cache/</link>
      <pubDate>Fri, 09 Oct 2026 15:00:00 +0800</pubDate>
      <author>root@beacox.space (BeaCox)</author>
      <guid>https://blog.beacox.space/posts/cross-cache/</guid>
      <description><br/>
  
  
  
  



&lt;div
  
    class=&#34;flex px-4 py-3 rounded-md bg-primary-100 dark:bg-primary-900&#34;
  
  &gt;
  &lt;span
    
      class=&#34;text-primary-400 ltr:pr-3 rtl:pl-3 flex items-center&#34;
    
    &gt;
    

  &lt;span class=&#34;relative block icon&#34;&gt;
    &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;
&lt;path fill=&#34;currentColor&#34; d=&#34;M234.7 42.7L197 56.8c-3 1.1-5 4-5 7.2s2 6.1 5 7.2l37.7 14.1L248.8 123c1.1 3 4 5 7.2 5s6.1-2 7.2-5l14.1-37.7L315 71.2c3-1.1 5-4 5-7.2s-2-6.1-5-7.2L277.3 42.7 263.2 5c-1.1-3-4-5-7.2-5s-6.1 2-7.2 5L234.7 42.7zM46.1 395.4c-18.7 18.7-18.7 49.1 0 67.9l34.6 34.6c18.7 18.7 49.1 18.7 67.9 0L529.9 116.5c18.7-18.7 18.7-49.1 0-67.9L495.3 14.1c-18.7-18.7-49.1-18.7-67.9 0L46.1 395.4zM484.6 82.6l-105 105-23.3-23.3 105-105 23.3 23.3zM7.5 117.2C3 118.9 0 123.2 0 128s3 9.1 7.5 10.8L64 160l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L128 160l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L128 96 106.8 39.5C105.1 35 100.8 32 96 32s-9.1 3-10.8 7.5L64 96 7.5 117.2zm352 256c-4.5 1.7-7.5 6-7.5 10.8s3 9.1 7.5 10.8L416 416l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L480 416l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L480 352l-21.2-56.5c-1.7-4.5-6-7.5-10.8-7.5s-9.1 3-10.8 7.5L416 352l-56.5 21.2z&#34;/&gt;&lt;/svg&gt;
  &lt;/span&gt;


  &lt;/span&gt;

  &lt;span
    
      class=&#34;dark:text-neutral-300&#34;
    
    &gt;本文由 AI 辅助编写，内容经人工校对。&lt;/span&gt;
&lt;/div&gt;

&lt;p&gt;pwn.college Kernel Exercise Collection 的第二题 Cache of Castaways，原题是 corCTF 2022 的同名题（作者 FizzBuzz101 / willsroot）。这题的套路是 cross-cache 溢出，本文就从这题出发，把 cross-cache 讲明白。&lt;/p&gt;
&lt;p&gt;靶机内核 &lt;code&gt;Linux 5.18.3&lt;/code&gt;，x86_64，单核，开了 SMEP/SMAP、KPTI、KASLR、&lt;code&gt;SLAB_FREELIST_RANDOM&lt;/code&gt;、&lt;code&gt;SLAB_FREELIST_HARDENED&lt;/code&gt;、&lt;code&gt;HARDENED_USERCOPY&lt;/code&gt;，关了 slab merge 和 &lt;code&gt;CONFIG_SYSVIPC&lt;/code&gt;。下面所有源码引用和数字都以这个版本为准。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;题目分析 
    &lt;div id=&#34;题目分析&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%a2%98%e7%9b%ae%e5%88%86%e6%9e%90&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;驱动有两个 ioctl：&lt;code&gt;ALLOC&lt;/code&gt; 从自己的 cache 分配一个 512 字节对象，&lt;code&gt;EDIT&lt;/code&gt; 往对象里 &lt;code&gt;memcpy&lt;/code&gt; 最多 512 字节。对象定义如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OVERFLOW_SZ 0x6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define CHUNK_SIZE  512
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pad&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OVERFLOW_SZ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;castaway_t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// buf 在 +0x6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;castaway_cache&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// 对象 512 字节
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;castaway_cachep&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;KMEM_CACHE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;castaway_cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLAB_PANIC&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLAB_ACCOUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;EDIT&lt;/code&gt; 的写入：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;memcpy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;castaway_arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;temp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// size &amp;lt;= 512，但 buf 从 +0x6 开始
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;buf&lt;/code&gt; 从对象 &lt;code&gt;+0x6&lt;/code&gt; 开始，却允许写满 512 字节，最后 6 字节越过对象末尾。是个线性堆溢出，但溢出半径只有 6 字节，而且这 6 字节大概率只落在同类对象上。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;页、slab、cache 的关系 
    &lt;div id=&#34;页slabcache-的关系&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%a1%b5slabcache-%e7%9a%84%e5%85%b3%e7%b3%bb&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;内核要动态内存，通常走两级分配器。下面是 buddy 分配整页、SLUB 切成对象：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;buddy      以页为单位，按 order 分配 2^order 个连续页（order-0 = 1 页 = 4KB）
  |  __get_free_pages / alloc_pages
SLUB       向 buddy 申请整页，切成固定大小的对象
  |  kmem_cache_alloc / kmalloc
object     kmalloc 拿到的那一小块
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;三个名词最容易混：&lt;code&gt;kmem_cache&lt;/code&gt;（也叫 slab cache）是一类对象的池子，有名字有固定大小；&lt;code&gt;slab&lt;/code&gt; 是这个 cache 从 buddy 要来的一页或几页，切成等长的槽；&lt;code&gt;object&lt;/code&gt; 是真正分配出去的那一块。这里的 cache 是对象池，下文一律写 kmem_cache 或 slab cache。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;order 是什么 
    &lt;div id=&#34;order-是什么&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#order-%e6%98%af%e4%bb%80%e4%b9%88&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;order 是连续页数量的 2 的指数。order-0 是 1 页（4KB），order-1 是 2 页，order-10 是 1024 页。申请 order-k 时，buddy 从 order-k 链表取；没有就从更高阶分裂，这由 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/page_alloc.c#L2250&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;expand()&lt;/code&gt;&lt;/a&gt; 完成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/page_alloc.c  expand()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;high&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;low&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;add_to_free_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;migratetype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 高地址那半挂到低阶链表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;set_buddy_order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;high&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它把请求的那块留在低地址，每一层的高地址那一半挂回对应低阶链表。于是从同一块大页里连续分裂出来的页，页号是升序连续的。这给了两个利用上的性质：同一批分配能拿到物理连续的页，所以能造出“页 i 和页 i+1 相邻”；但物理相邻不等于地址可知，KASLR 开着也拿不到页的物理地址，而 cross-cache 只需要“相邻”，不需要“地址已知”。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;一页装几个对象 
    &lt;div id=&#34;一页装几个对象&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e4%b8%80%e9%a1%b5%e8%a3%85%e5%87%a0%e4%b8%aa%e5%af%b9%e8%b1%a1&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;castaway 对象 512 字节，为什么每页正好 8 个（order-0）？看 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slub.c#L3817&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;calculate_order()&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/slub.c  calculate_order()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;min_objects&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slub_min_objects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;min_objects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;nr_cpus&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;num_present_cpus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nr_cpus&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;nr_cpus&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nr_cpu_ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;min_objects&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;fls&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nr_cpus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 1 CPU -&amp;gt; 4*(1+1) = 8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;默认希望一个 slab 至少装 8 个对象。512 字节一页正好 8 个，order-0。靶机 root shell 里 &lt;code&gt;cat /proc/slabinfo&lt;/code&gt; 能直接看到这些数字：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# name          &amp;lt;active_objs&amp;gt; &amp;lt;num_objs&amp;gt; &amp;lt;objsize&amp;gt; &amp;lt;objperslab&amp;gt; &amp;lt;pagesperslab&amp;gt;
castaway_cache     240    240    512    8    1      &amp;lt;- 512B，每页 8 个，order-0
cred_jar           480    480    128   32    1      &amp;lt;- 128B，每页 32 个，order-0
signal_cache       448    448    960    8    2      &amp;lt;- 960B 一页放不下 8 个，order-1
task_struct        451    451   2752   11    8      &amp;lt;- order-3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;多核机器上同一个 cache 的 order 会变大（对象更多、减少锁竞争），这正是很多 exp 要 &lt;code&gt;sched_setaffinity&lt;/code&gt; 绑核的原因。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;通用 cache 与专用 cache 
    &lt;div id=&#34;通用-cache-与专用-cache&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%80%9a%e7%94%a8-cache-%e4%b8%8e%e4%b8%93%e7%94%a8-cache&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;kmem_cache 分两类。通用 cache 是 &lt;code&gt;kmalloc-8/16/.../512/1k/...&lt;/code&gt;，&lt;code&gt;kmalloc(200)&lt;/code&gt; 会从 &lt;code&gt;kmalloc-256&lt;/code&gt; 切一块。专用 cache 是某个子系统用 &lt;code&gt;kmem_cache_create&lt;/code&gt; 单独建的，比如 &lt;code&gt;cred_jar&lt;/code&gt;（存 &lt;code&gt;struct cred&lt;/code&gt;）、&lt;code&gt;task_struct&lt;/code&gt;、还有本题驱动建的 &lt;code&gt;castaway_cache&lt;/code&gt;。另外带 &lt;code&gt;SLAB_ACCOUNT&lt;/code&gt; / &lt;code&gt;GFP_KERNEL_ACCOUNT&lt;/code&gt; 的分配走的是单独一套 &lt;code&gt;kmalloc-cg-*&lt;/code&gt;（被 memcg 登记），和 &lt;code&gt;kmalloc-*&lt;/code&gt; 并存。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;pcp 与 LIFO 
    &lt;div id=&#34;pcp-与-lifo&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#pcp-%e4%b8%8e-lifo&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;order-0 页的分配释放有一条快车道：buddy 下面给每个 CPU 挂了一小堆空闲页（per-CPU pageset，pcp），拿页还页先走这里，避开 zone 锁。释放走 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/page_alloc.c#L3383&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;free_unref_page_commit()&lt;/code&gt;&lt;/a&gt;，&lt;strong&gt;头插&lt;/strong&gt;进 pcp：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/page_alloc.c  free_unref_page_commit()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;pcp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;this_cpu_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;zone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;per_cpu_pageset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;pindex&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;order_to_pindex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;migratetype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;list_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lru&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pindex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 头插，LIFO
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;pcp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;分配时从&lt;strong&gt;头部&lt;/strong&gt;取，所以最近释放的页最先被复用（LIFO）。但 pcp 不能无限涨：它有个上限 &lt;code&gt;high&lt;/code&gt;（&lt;code&gt;pcp-&amp;gt;high&lt;/code&gt;，由 &lt;code&gt;nr_pcp_high()&lt;/code&gt; 按 zone 大小和在线 CPU 数算出，可用 &lt;code&gt;vm.percpu_pagelist_high_fraction&lt;/code&gt; 调，&lt;code&gt;cat /proc/zoneinfo&lt;/code&gt; 能看每个 CPU 的实际 &lt;code&gt;high&lt;/code&gt;/&lt;code&gt;batch&lt;/code&gt;；注意这是 pcp 自己的上限，跟 zone 的 &lt;code&gt;min/low/high&lt;/code&gt; 水位——那套管 kswapd / 内存回收——不是一回事）。一旦 &lt;code&gt;pcp-&amp;gt;count &amp;gt;= high&lt;/code&gt;，就调 &lt;code&gt;free_pcppages_bulk()&lt;/code&gt; 从&lt;strong&gt;尾部&lt;/strong&gt;（最老的那头）一次拿走 &lt;code&gt;batch&lt;/code&gt; 张还给 buddy：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/page_alloc.c  free_pcppages_bulk()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;page&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;list_last_entry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lru&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 老页还给 buddy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结论：order-0 页的分配释放，就是每个 CPU、每种 migratetype 一条&lt;strong&gt;有上限的 LIFO 队列&lt;/strong&gt;——没满时按 LIFO 复用，满了就把最老的一批挤回 buddy。本题 exp 的关键假设建在前半句上：先释放奇数页、再释放偶数页，最后释放的那批还在 pcp 头部、按 LIFO 被下一批分配拿走。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;migratetype：为什么喷页用内核对象而不是 mmap 
    &lt;div id=&#34;migratetype为什么喷页用内核对象而不是-mmap&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#migratetype%e4%b8%ba%e4%bb%80%e4%b9%88%e5%96%b7%e9%a1%b5%e7%94%a8%e5%86%85%e6%a0%b8%e5%af%b9%e8%b1%a1%e8%80%8c%e4%b8%8d%e6%98%af-mmap&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;buddy 还按“能不能搬走”给页分类，pageblock 粒度管理。slab 页来自 &lt;code&gt;Unmovable&lt;/code&gt;；用户匿名页、page cache 是 &lt;code&gt;Movable&lt;/code&gt;，&lt;code&gt;mmap&lt;/code&gt; 拿到的是后者。不同 migratetype 各有空闲链表，所以用 &lt;code&gt;mmap&lt;/code&gt;/&lt;code&gt;malloc&lt;/code&gt; 的用户页去喷，它们大概率和 slab 页不在同一批空闲链里，做不成邻居。本题用 &lt;code&gt;PACKET_TX_RING&lt;/code&gt;（内核走 &lt;code&gt;__get_free_pages(GFP_KERNEL)&lt;/code&gt;，是 Unmovable）就是为了精确控制 Unmovable 页的生与死。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;为什么只能 cross-cache 
    &lt;div id=&#34;为什么只能-cross-cache&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e4%b8%ba%e4%bb%80%e4%b9%88%e5%8f%aa%e8%83%bd-cross-cache&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;改 freelist 走不通。一个 slab 里空闲对象串成单链表，指针存哪由 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slub.c#L4054&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;calculate_sizes()&lt;/code&gt;&lt;/a&gt; 决定：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/slub.c  calculate_sizes()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;         * Store freelist pointer near middle of object to keep
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;         * it away from the edges of the object to avoid small
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;         * sized over/underflows from neighboring allocations.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;         */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ALIGN_DOWN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;512 字节对象的 freelist 指针在偏移 256，对象正中，不在开头。这是 5.7 为了防小范围越界专门挪的位置，越界 6 字节根本碰不到。就算碰到了，&lt;code&gt;SLAB_FREELIST_HARDENED&lt;/code&gt; 还做了混淆，&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slub.c#L309&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;freelist_ptr()&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/slub.c  freelist_ptr()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)((&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ptr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;swab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;kasan_reset_tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ptr_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;要伪造合法值得同时知道 per-cache 随机数、指针存放地址、下一个空闲对象地址。加上 &lt;code&gt;SLAB_FREELIST_RANDOM&lt;/code&gt; 把初始空闲顺序打乱，这条路在这个内核上基本死了。&lt;/p&gt;
&lt;p&gt;溢出到好用的对象也走不通。内核关了 &lt;code&gt;CONFIG_SYSVIPC&lt;/code&gt;，没有 &lt;code&gt;msg_msg&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;更关键的是对象根本不和别人共享 cache。建 cache 时 &lt;code&gt;kmem_cache_create&lt;/code&gt; 先调 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slab_common.c#L186&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;find_mergeable()&lt;/code&gt;&lt;/a&gt; 看能不能复用现成的同规格 cache，但这题合并被禁了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// mm/slab_common.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slab_nomerge&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IS_ENABLED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CONFIG_SLAB_MERGE_DEFAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 没开就是 true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;slab_unmergeable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kmem_cache&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slab_nomerge&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLAB_NEVER_MERGE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;usersize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// kmalloc cache 带 usersize（hardened usercopy），永不被合并
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;CONFIG_SLAB_MERGE_DEFAULT&lt;/code&gt; 没开，&lt;code&gt;find_mergeable()&lt;/code&gt; 直接返回 NULL；加上 &lt;code&gt;SLAB_ACCOUNT&lt;/code&gt; 和 &lt;code&gt;castaway_cache&lt;/code&gt; 本身属性对不上，它是一个独立的 kmem_cache，同一张 slab 页里全是 castaway 对象。溢出 6 字节只会踩到同类下一个对象的 pad，没有意义。&lt;/p&gt;
&lt;p&gt;唯一有价值的落点，是一张 slab 页里最后一个对象的溢出。那 6 字节越过页边界，落到下一张物理页的开头。cross-cache 就从这里开始：既然 cache 内部没活了，就让溢出跨过 cache 的边界，落到另一个 cache 的页上。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;cross-cache 的原理 
    &lt;div id=&#34;cross-cache-的原理&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#cross-cache-%e7%9a%84%e5%8e%9f%e7%90%86&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;把溢出按落点分两种。溢出对象不是本页最后一个时，落点是同页下一个对象，还在本 cache 内，本题里就是 8 个对象的前 7 个，踩到同类 pad，无害。溢出对象是本页最后一个、且下一张物理页属于另一个 cache 时，落点是下一页开头，这才是 cross-cache：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;对象级（普通 heap pwn）           页级（cross-cache）
[ A | A | A | A ]                [  cache A 的页  |  cache B 的页  ]
越界踩到同类邻居                  A 页最后一个对象越界 -&amp;gt; 命中 B 页开头的对象
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所以 cross-cache 不是新的漏洞原语，就是普通堆溢出加一个布置好的物理页邻接关系。漏洞提供越过页边界的那几个字节，利用者负责把页边界另一边换成一个有价值的受害者对象。难点全在后半句：怎么让受害者 cache 的页稳稳落在溢出 cache 页的正上方（地址 +0x1000 处）。这就是页风水。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;页风水 
    &lt;div id=&#34;页风水&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%a1%b5%e9%a3%8e%e6%b0%b4&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;目标是造出 &lt;code&gt;[castaway 页][cred 页]&lt;/code&gt; 这样物理相邻、且 cred 页在高地址侧的一对，分为以下四步。&lt;/p&gt;
&lt;p&gt;第一步，找一个非特权能稳定、大量、可单独释放 order-0 页的原语。本题用 &lt;code&gt;setsockopt(PACKET_TX_RING)&lt;/code&gt;，走到 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/net/packet/af_packet.c#L4327&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;alloc_pg_vec&lt;/code&gt;&lt;/a&gt; 再到 &lt;code&gt;alloc_one_pg_vec_page&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// net/packet/af_packet.c  alloc_one_pg_vec_page
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__get_free_pages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gfp_flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 每个 socket 吃掉整页
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对应用户态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;socket&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AF_PACKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SOCK_RAW&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;htons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ETH_P_ALL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TPACKET_V1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;setsockopt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SOL_PACKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PACKET_VERSION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tpacket_req&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;req&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tp_block_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4096&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tp_block_nr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                           &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tp_frame_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4096&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tp_frame_nr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;setsockopt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SOL_PACKET&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PACKET_TX_RING&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;req&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 申请 1 张 order-0 页
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// close(s) 即释放
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;PACKET_TX_RING&lt;/code&gt; 需要 &lt;code&gt;CAP_NET_RAW&lt;/code&gt;，所以喷页逻辑放在 &lt;code&gt;unshare(CLONE_NEWUSER | CLONE_NEWNET)&lt;/code&gt; 的子进程里，空 userns 不映射 uid_map 也能在新 net namespace 里拿到这个 cap。&lt;/p&gt;
&lt;p&gt;第二步，隔一个释放一个。喷 1000 页后释放所有奇数下标的页：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;spray :  [p0][p1][p2][p3][p4][p5] ...   全是我们控制的页
free odd:[p0][  ][p2][  ][p4][  ] ...   奇数页还给 buddy/pcp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里必须隔一释放。连续释放一整段，buddy 的伙伴空闲会把它们合并回 order-1/2，页就“消失”了，下一批分配从大块重新分裂，相邻关系不再可控。隔一释放留下占位页，伙伴不空闲、无法合并，每张空闲页都是稳定的 order-0，等着被别的 cache 精确插进来。&lt;/p&gt;
&lt;p&gt;第三步，让受害者 cache 因为手头没有空闲对象、被迫向 buddy 要新页，捡走刚释放的奇数页。这里有个前置动作：SLUB 优先用当前 slab 和 partial slab 里的空闲对象，如果 &lt;code&gt;cred_jar&lt;/code&gt; 还有半满的页，新 cred 会填到老页上，根本不来要新页，页风水就白做了。所以先 &lt;code&gt;fork&lt;/code&gt; 100 个睡眠子进程把已有 &lt;code&gt;cred_jar&lt;/code&gt; slab 填满。受害者 cache 是 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/kernel/cred.c#L692&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;cred_jar&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// kernel/cred.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;cred_jar&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kmem_cache_create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cred_jar&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;SLAB_HWCACHE_ALIGN&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLAB_PANIC&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SLAB_ACCOUNT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kmem_cache_zalloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cred_jar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GFP_KERNEL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 每次 fork/clone 走这里
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后 &lt;code&gt;clone&lt;/code&gt; 320 个子进程，它们的 cred 落在奇数页上。&lt;code&gt;fork&lt;/code&gt; 一次会触发十几个 cache 的分配（task_struct、内核栈、vm_area_struct、files/fs/sighand/signal cache、pid……），噪声大，用 &lt;code&gt;clone&lt;/code&gt; 加 flag 砍掉大部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define CLONE_FLAGS (CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;代价是 &lt;code&gt;CLONE_VM&lt;/code&gt; 让子进程和父进程共享地址空间，所以子进程里不能随便用自己的栈和全局变量，本题的等待与提权逻辑是纯内联汇编加全局缓冲区实现的，就是为了防止共享地址空间被破坏造成segfault。&lt;/p&gt;
&lt;p&gt;第四步，释放所有偶数页，再用 castaway slab 把它们取回来。偶数页 &lt;code&gt;i&lt;/code&gt; 和奇数页 &lt;code&gt;i+1&lt;/code&gt; 物理相邻，布局就成了：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;偶数页 i   (castaway)   第 8 个对象向上溢出 6 字节
奇数页 i+1 (cred)       页首是一个 struct cred
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;隔行（odd/even）的意义就是让溢出页和受害者页交替相邻。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;受害者：cred 
    &lt;div id=&#34;受害者cred&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e5%8f%97%e5%ae%b3%e8%80%85cred&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;struct cred&lt;/code&gt; 128 字节（&lt;code&gt;SLAB_HWCACHE_ALIGN&lt;/code&gt; 把 &lt;code&gt;sizeof(struct cred)&lt;/code&gt; 向上对齐到 cache line），每页 32 个，页开头就是对象开头，所以溢出永远精确命中下一页第一个 cred 的 &lt;code&gt;+0x0&lt;/code&gt;。看 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/include/linux/cred.h#L110&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;struct cred&lt;/code&gt;&lt;/a&gt; 开头（本题关了 &lt;code&gt;CONFIG_DEBUG_CREDENTIALS&lt;/code&gt; 和 RANDSTRUCT，所以 &lt;code&gt;usage&lt;/code&gt; 真的在 &lt;code&gt;+0x0&lt;/code&gt;）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cred&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;atomic_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;usage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// +0x0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;kuid_t&lt;/span&gt;   &lt;span class=&#34;n&#34;&gt;uid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// +0x4  &amp;lt;- 要清零的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;kgid_t&lt;/span&gt;   &lt;span class=&#34;n&#34;&gt;gid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// +0x8
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;6 字节正好是 &lt;code&gt;usage&lt;/code&gt;(4) 加 &lt;code&gt;uid&lt;/code&gt; 的低 16 位(2)。溢出数据：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;evil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;512&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;evil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;512&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 伪造 usage = 0x100，别让引用计数归零
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// evil[512-2 .. 512-1] 保持 0  -&amp;gt;  uid 低 16 位清零，uid 1000(0x3e8) -&amp;gt; 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;uid&lt;/code&gt; 变成 0 之后不需要泄露 KASLR，也不用改 caps：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;setresuid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 无 CAP_SETUID 时，目标 uid == cred-&amp;gt;uid 才合法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;内核认为进程本来就是 uid 0，这个 &lt;code&gt;setresuid&lt;/code&gt; 合法通过，euid/fsuid 一起变 0。读文件权限看的是 &lt;code&gt;fsuid&lt;/code&gt; 和 caps，不是 &lt;code&gt;uid&lt;/code&gt;，&lt;code&gt;fsuid == 0&lt;/code&gt; 且文件属主是 root，就能读 &lt;code&gt;0400&lt;/code&gt; 的 &lt;code&gt;/flag&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;从这题可以总结出挑受害者的三条标准：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;头部就有高价值字段，因为溢出只精确落在页首对象的开头，cred 的 &lt;code&gt;usage&lt;/code&gt;/&lt;code&gt;uid&lt;/code&gt; 恰好在最前面&lt;/li&gt;
&lt;li&gt;能被非特权大量可控分配，&lt;code&gt;fork&lt;/code&gt;/&lt;code&gt;clone&lt;/code&gt; 要多少 cred 有多少&lt;/li&gt;
&lt;li&gt;改完能 leakless 变现，uid 清零加 &lt;code&gt;setresuid&lt;/code&gt; 这条链不依赖泄露。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;cred 之外常见的受害者还有 &lt;code&gt;struct file&lt;/code&gt;、&lt;code&gt;pipe_buffer&lt;/code&gt;、页表项（配合 Dirty Pagetable）。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;完整利用 
    &lt;div id=&#34;完整利用&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e5%ae%8c%e6%95%b4%e5%88%a9%e7%94%a8&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;把上面串起来就是 exp 的六步：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[1] drain    fork 100 个睡眠进程，填满已有 cred_jar slab
[2] spray    TX_RING 喷 1000 页（user+net ns 子进程里），释放奇数页
[3] cred     clone 320 个子进程，cred 落在奇数页；子进程阻塞在 read(rootfd)
[4] reclaim  释放偶数页
[5] overflow 30 页 castaway（240 个对象 &amp;lt;= 驱动上限 400），每个溢出 6 字节
[6] wake     写 rootfd 唤醒子进程；uid==0 的那个 setresuid(0,0,0) 读 /flag
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 class=&#34;relative group&#34;&gt;方法二：cross-cache UAF / 页复用（本题没用到） 
    &lt;div id=&#34;方法二cross-cache-uaf--页复用本题没用到&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e6%96%b9%e6%b3%95%e4%ba%8ccross-cache-uaf--%e9%a1%b5%e5%a4%8d%e7%94%a8%e6%9c%ac%e9%a2%98%e6%b2%a1%e7%94%a8%e5%88%b0&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;前面整篇讲的是 cross-cache &lt;strong&gt;溢出&lt;/strong&gt;：bug 是越界写，靠页风水把两张&lt;strong&gt;不同&lt;/strong&gt;的页排成物理相邻，越界字节跨过页边界打到邻居。但 cross-cache 还有另一条主路，用的 bug 类型和 buddy 性质都不一样，很多 UAF 提权（典型是 DirtyCred）走的是这条。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;原语不同：UAF，而不是 OOB 
    &lt;div id=&#34;原语不同uaf而不是-oob&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e5%8e%9f%e8%af%ad%e4%b8%8d%e5%90%8cuaf%e8%80%8c%e4%b8%8d%e6%98%af-oob&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;方法二的 bug 是 use-after-free / double-free：手里有一个指向 cache A 对象的&lt;strong&gt;悬垂指针&lt;/strong&gt;。单纯在 A 里重新分配（把对象 free 掉再 alloc 一个 A 对象）只会让悬垂指针压在一个&lt;strong&gt;同类&lt;/strong&gt; A 对象上，还在 A 内部，谈不上 cross。要跨到 B，必须让这块物理内存&lt;strong&gt;整个离开 cache A、回到 buddy，再被 cache B 抢走&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;难点：怎么让整张 slab 掉回 buddy 
    &lt;div id=&#34;难点怎么让整张-slab-掉回-buddy&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%9a%be%e7%82%b9%e6%80%8e%e4%b9%88%e8%ae%a9%e6%95%b4%e5%bc%a0-slab-%e6%8e%89%e5%9b%9e-buddy&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;SLUB 只要 slab 里还有一个对象在用就不放页；而且就算整张清空，页还可能被 per-CPU 缓存住不还。看 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/A/ident/__slab_free&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;__slab_free()&lt;/code&gt;&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;kfree 掉 slab 上最后一个在用对象
  └─ __slab_free(): inuse == 0            // 整张 slab 空了
       ├─ 它是当前 c-&amp;gt;slab(活跃 slab)         -&amp;gt; 留着当快车道，不还    ← 坑
       ├─ 进 c-&amp;gt;partial 且没超 per-cpu 上限     -&amp;gt; 当半满缓存留着        ← 坑
       └─ 否则 remove_partial -&amp;gt; discard_slab
            └─ free_slab -&amp;gt; __free_pages() -&amp;gt; order-0 进 pcp -&amp;gt; 回 buddy ✅
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所以方法二不能只 free 一个对象，得&lt;strong&gt;批量&lt;/strong&gt;：喷大量 A 对象，让它们铺满若干张&lt;strong&gt;全新&lt;/strong&gt;的 slab；再把这些 slab 整张整张全部 free，使空 slab 的数量&lt;strong&gt;超过&lt;/strong&gt; per-cpu partial（&lt;code&gt;slub_cpu_partial()&lt;/code&gt;）和 node partial（&lt;code&gt;s-&amp;gt;min_partial&lt;/code&gt;）的缓存上限——多出来的空页才会被 &lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/A/ident/discard_slab&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;discard_slab()&lt;/code&gt;&lt;/a&gt; 真正交还 buddy。&lt;/p&gt;

&lt;h3 class=&#34;relative group&#34;&gt;buddy 性质不同：要的是「同页 LIFO 复用」，不是「相邻」 
    &lt;div id=&#34;buddy-性质不同要的是同页-lifo-复用不是相邻&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#buddy-%e6%80%a7%e8%b4%a8%e4%b8%8d%e5%90%8c%e8%a6%81%e7%9a%84%e6%98%af%e5%90%8c%e9%a1%b5-lifo-%e5%a4%8d%e7%94%a8%e4%b8%8d%e6%98%af%e7%9b%b8%e9%82%bb&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;页一旦回到 buddy，order-0 先落在 pcp 头部（LIFO，见前面「pcp 与 LIFO」）。紧接着立刻逼 cache B 要新页：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;free 满整张 A 的 slab    -&amp;gt;  页进 pcp 头部(LIFO)
立刻耗尽 B 的 current + partial，再 alloc 一个 B
  -&amp;gt; new_slab -&amp;gt; allocate_slab -&amp;gt; alloc_pages 拿到 pcp 头 = 刚释放那张页
  -&amp;gt; B 把它重切成 B 对象
  -&amp;gt; 悬垂的 A 指针现在压在一个 B 对象上 = 跨 cache 类型混淆
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;方法二只依赖 buddy 的两个性质：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;order-0 走 pcp、最近释放的最先发出（LIFO），保证“刚还的页被 B 立刻捡走”；&lt;/li&gt;
&lt;li&gt;上面那张 slab 的 &lt;code&gt;discard_slab&lt;/code&gt; 释放条件，保证页真回到了 buddy，而不是被 SLUB 藏在 per-cpu/partial 里。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 class=&#34;relative group&#34;&gt;拿到类型混淆之后 
    &lt;div id=&#34;拿到类型混淆之后&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e6%8b%bf%e5%88%b0%e7%b1%bb%e5%9e%8b%e6%b7%b7%e6%b7%86%e4%b9%8b%e5%90%8e&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h3&gt;
&lt;p&gt;悬垂 A 指针与 B 对象重叠后，就回到常规 UAF 套路：用 A 的接口&lt;strong&gt;读&lt;/strong&gt; B 对象 = 跨类型泄露（泄 KASLR / 指针），用 A 的接口&lt;strong&gt;写&lt;/strong&gt; B 对象 = 跨类型改字段。DirtyCred 是最有名的落地：UAF/double-free 一个 &lt;code&gt;struct file&lt;/code&gt; 或 &lt;code&gt;cred&lt;/code&gt;，把整页释放回收后换成一个权限不同的同类对象（只读 fd 换可写 fd、低权 cred 换高权 cred），不碰指针也能提权——和本题“只改数据字段”的无泄露思路异曲同工，只是跨 cache 的桥是“页复用”而非“页相邻”。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;防御措施 
    &lt;div id=&#34;防御措施&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e9%98%b2%e5%be%a1%e6%8e%aa%e6%96%bd&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;p&gt;cross-cache 成为主流，是因为内核把 cache 内部的路一条条堵上了。&lt;code&gt;SLAB_FREELIST_RANDOM&lt;/code&gt; / &lt;code&gt;_HARDENED&lt;/code&gt; 加上 freelist 指针挪到对象中间，封死了 cache 内改 freelist，cross-cache 直接绕过，根本不碰 freelist。&lt;/p&gt;
&lt;p&gt;防御也在跟进。&lt;code&gt;CONFIG_RANDOM_KMALLOC_CACHES&lt;/code&gt;（5.19+，本题内核没有）把 &lt;code&gt;kmalloc-N&lt;/code&gt; 拆成多个随机 cache，增加通用 cache 内部碰撞难度，但对 &lt;code&gt;cred_jar&lt;/code&gt; 这种专用 cache 的 cross-cache 没有直接影响。社区提案的 &lt;code&gt;SLAB_VIRTUAL&lt;/code&gt; 给每个 slab 的虚拟地址做隔离、让释放的 slab 虚拟地址不被别的 cache 立即复用，直接打击“释放页、受害者立刻捡走”这一步，是目前最对症的方向。grsecurity 的 AUTOSLAB 走的也是削弱 pcp/buddy 可预测性的思路。&lt;/p&gt;
&lt;p&gt;但只要物理页还在 cache 之间回收复用、pcp 还近似 LIFO，页风水的前提就还在。cross-cache 现在更费劲但依然可行。&lt;/p&gt;

&lt;h2 class=&#34;relative group&#34;&gt;参考资料 
    &lt;div id=&#34;参考资料&#34; class=&#34;anchor&#34;&gt;&lt;/div&gt;
    
    &lt;span
        class=&#34;absolute top-0 w-6 transition-opacity opacity-0 ltr:-left-6 rtl:-right-6 not-prose group-hover:opacity-100&#34;&gt;
        &lt;a class=&#34;group-hover:text-primary-300 dark:group-hover:text-neutral-700 !no-underline&#34; href=&#34;#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99&#34; aria-label=&#34;Anchor&#34;&gt;#&lt;/a&gt;
    &lt;/span&gt;        
    
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a
  href=&#34;https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html&#34;
    target=&#34;_blank&#34;
  &gt;Reviving Exploits Against cred Structs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
  href=&#34;https://github.com/Markakd/DirtyCred&#34;
    target=&#34;_blank&#34;
  &gt;DirtyCred（方法二的代表，含 PoC）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
  href=&#34;https://n132.github.io/2024/06/28/Castaways.html&#34;
    target=&#34;_blank&#34;
  &gt;Castaways 复现笔记&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a
  href=&#34;https://github.com/Crusaders-of-Rust/corCTF-2022-public-challenge-archive/tree/master/pwn/cache-of-castaways&#34;
    target=&#34;_blank&#34;
  &gt;corCTF 2022 题目归档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;源码：&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slub.c&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;mm/slub.c&lt;/code&gt;&lt;/a&gt;、&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/page_alloc.c&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;mm/page_alloc.c&lt;/code&gt;&lt;/a&gt;、&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/mm/slab_common.c&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;mm/slab_common.c&lt;/code&gt;&lt;/a&gt;、&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/kernel/cred.c&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;kernel/cred.c&lt;/code&gt;&lt;/a&gt;、&lt;a
  href=&#34;https://elixir.bootlin.com/linux/v5.18.3/source/net/packet/af_packet.c&#34;
    target=&#34;_blank&#34;
  &gt;&lt;code&gt;net/packet/af_packet.c&lt;/code&gt;&lt;/a&gt;（Linux 5.18.3）&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
  </channel>
</rss>
