CVE-2022-49700
Severity CVSS v4.0:
Pending analysis
Type:
CWE-416
Use After Free
Publication date:
26/02/2025
Last modified:
25/03/2025
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
mm/slub: add missing TID updates on slab deactivation<br />
<br />
The fastpath in slab_alloc_node() assumes that c->slab is stable as long as<br />
the TID stays the same. However, two places in __slab_alloc() currently<br />
don&#39;t update the TID when deactivating the CPU slab.<br />
<br />
If multiple operations race the right way, this could lead to an object<br />
getting lost; or, in an even more unlikely situation, it could even lead to<br />
an object being freed onto the wrong slab&#39;s freelist, messing up the<br />
`inuse` counter and eventually causing a page to be freed to the page<br />
allocator while it still contains slab objects.<br />
<br />
(I haven&#39;t actually tested these cases though, this is just based on<br />
looking at the code. Writing testcases for this stuff seems like it&#39;d be<br />
a pain...)<br />
<br />
The race leading to state inconsistency is (all operations on the same CPU<br />
and kmem_cache):<br />
<br />
- task A: begin do_slab_free():<br />
- read TID<br />
- read pcpu freelist (==NULL)<br />
- check `slab == c->slab` (true)<br />
- [PREEMPT A->B]<br />
- task B: begin slab_alloc_node():<br />
- fastpath fails (`c->freelist` is NULL)<br />
- enter __slab_alloc()<br />
- slub_get_cpu_ptr() (disables preemption)<br />
- enter ___slab_alloc()<br />
- take local_lock_irqsave()<br />
- read c->freelist as NULL<br />
- get_freelist() returns NULL<br />
- write `c->slab = NULL`<br />
- drop local_unlock_irqrestore()<br />
- goto new_slab<br />
- slub_percpu_partial() is NULL<br />
- get_partial() returns NULL<br />
- slub_put_cpu_ptr() (enables preemption)<br />
- [PREEMPT B->A]<br />
- task A: finish do_slab_free():<br />
- this_cpu_cmpxchg_double() succeeds()<br />
- [CORRUPT STATE: c->slab==NULL, c->freelist!=NULL]<br />
<br />
From there, the object on c->freelist will get lost if task B is allowed to<br />
continue from here: It will proceed to the retry_load_slab label,<br />
set c->slab, then jump to load_freelist, which clobbers c->freelist.<br />
<br />
But if we instead continue as follows, we get worse corruption:<br />
<br />
- task A: run __slab_free() on object from other struct slab:<br />
- CPU_PARTIAL_FREE case (slab was on no list, is now on pcpu partial)<br />
- task A: run slab_alloc_node() with NUMA node constraint:<br />
- fastpath fails (c->slab is NULL)<br />
- call __slab_alloc()<br />
- slub_get_cpu_ptr() (disables preemption)<br />
- enter ___slab_alloc()<br />
- c->slab is NULL: goto new_slab<br />
- slub_percpu_partial() is non-NULL<br />
- set c->slab to slub_percpu_partial(c)<br />
- [CORRUPT STATE: c->slab points to slab-1, c->freelist has objects<br />
from slab-2]<br />
- goto redo<br />
- node_match() fails<br />
- goto deactivate_slab<br />
- existing c->freelist is passed into deactivate_slab()<br />
- inuse count of slab-1 is decremented to account for object from<br />
slab-2<br />
<br />
At this point, the inuse count of slab-1 is 1 lower than it should be.<br />
This means that if we free all allocated objects in slab-1 except for one,<br />
SLUB will think that slab-1 is completely unused, and may free its page,<br />
leading to use-after-free.
Impact
Base Score 3.x
7.80
Severity 3.x
HIGH
Vulnerable products and versions
CPE | From | Up to |
---|---|---|
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 3.1 (including) | 4.9.323 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 4.10 (including) | 4.14.288 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 4.15 (including) | 4.19.252 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 4.20 (including) | 5.4.205 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 5.5 (including) | 5.10.130 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 5.11 (including) | 5.15.54 (excluding) |
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 5.16 (including) | 5.18.8 (excluding) |
cpe:2.3:o:linux:linux_kernel:5.19:rc1:*:*:*:*:*:* | ||
cpe:2.3:o:linux:linux_kernel:5.19:rc2:*:*:*:*:*:* | ||
cpe:2.3:o:linux:linux_kernel:5.19:rc3:*:*:*:*:*:* |
To consult the complete list of CPE names with products and versions, see this page
References to Advisories, Solutions, and Tools
- https://git.kernel.org/stable/c/0515cc9b6b24877f59b222ade704bfaa42caa2a6
- https://git.kernel.org/stable/c/197e257da473c725dfe47759c3ee02f2398d8ea5
- https://git.kernel.org/stable/c/308c6d0e1f200fd26c71270c6e6bfcf0fc6ff082
- https://git.kernel.org/stable/c/6c32496964da0dc230cea763a0e934b2e02dabd5
- https://git.kernel.org/stable/c/d6a597450e686d4c6388bd3cdcb17224b4dae7f0
- https://git.kernel.org/stable/c/e2b2f0e2e34d71ae6c2a1114fd3c525930e84bc7
- https://git.kernel.org/stable/c/e7e3e90d671078455a3a08189f89d85b3da2de9e
- https://git.kernel.org/stable/c/eeaa345e128515135ccb864c04482180c08e3259