CVE-2024-50194

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
08/11/2024
Last modified:
03/11/2025

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> arm64: probes: Fix uprobes for big-endian kernels<br /> <br /> The arm64 uprobes code is broken for big-endian kernels as it doesn&amp;#39;t<br /> convert the in-memory instruction encoding (which is always<br /> little-endian) into the kernel&amp;#39;s native endianness before analyzing and<br /> simulating instructions. This may result in a few distinct problems:<br /> <br /> * The kernel may may erroneously reject probing an instruction which can<br /> safely be probed.<br /> <br /> * The kernel may erroneously erroneously permit stepping an<br /> instruction out-of-line when that instruction cannot be stepped<br /> out-of-line safely.<br /> <br /> * The kernel may erroneously simulate instruction incorrectly dur to<br /> interpretting the byte-swapped encoding.<br /> <br /> The endianness mismatch isn&amp;#39;t caught by the compiler or sparse because:<br /> <br /> * The arch_uprobe::{insn,ixol} fields are encoded as arrays of u8, so<br /> the compiler and sparse have no idea these contain a little-endian<br /> 32-bit value. The core uprobes code populates these with a memcpy()<br /> which similarly does not handle endianness.<br /> <br /> * While the uprobe_opcode_t type is an alias for __le32, both<br /> arch_uprobe_analyze_insn() and arch_uprobe_skip_sstep() cast from u8[]<br /> to the similarly-named probe_opcode_t, which is an alias for u32.<br /> Hence there is no endianness conversion warning.<br /> <br /> Fix this by changing the arch_uprobe::{insn,ixol} fields to __le32 and<br /> adding the appropriate __le32_to_cpu() conversions prior to consuming<br /> the instruction encoding. The core uprobes copies these fields as opaque<br /> ranges of bytes, and so is unaffected by this change.<br /> <br /> At the same time, remove MAX_UINSN_BYTES and consistently use<br /> AARCH64_INSN_SIZE for clarity.<br /> <br /> Tested with the following:<br /> <br /> | #include <br /> | #include <br /> |<br /> | #define noinline __attribute__((noinline))<br /> |<br /> | static noinline void *adrp_self(void)<br /> | {<br /> | void *addr;<br /> |<br /> | asm volatile(<br /> | " adrp %x0, adrp_self\n"<br /> | " add %x0, %x0, :lo12:adrp_self\n"<br /> | : "=r" (addr));<br /> | }<br /> |<br /> |<br /> | int main(int argc, char *argv)<br /> | {<br /> | void *ptr = adrp_self();<br /> | bool equal = (ptr == adrp_self);<br /> |<br /> | printf("adrp_self =&gt; %p\n"<br /> | "adrp_self() =&gt; %p\n"<br /> | "%s\n",<br /> | adrp_self, ptr, equal ? "EQUAL" : "NOT EQUAL");<br /> |<br /> | return 0;<br /> | }<br /> <br /> .... where the adrp_self() function was compiled to:<br /> <br /> | 00000000004007e0 :<br /> | 4007e0: 90000000 adrp x0, 400000 <br /> | 4007e4: 911f8000 add x0, x0, #0x7e0<br /> | 4007e8: d65f03c0 ret<br /> <br /> Before this patch, the ADRP is not recognized, and is assumed to be<br /> steppable, resulting in corruption of the result:<br /> <br /> | # ./adrp-self<br /> | adrp_self =&gt; 0x4007e0<br /> | adrp_self() =&gt; 0x4007e0<br /> | EQUAL<br /> | # echo &amp;#39;p /root/adrp-self:0x007e0&amp;#39; &gt; /sys/kernel/tracing/uprobe_events<br /> | # echo 1 &gt; /sys/kernel/tracing/events/uprobes/enable<br /> | # ./adrp-self<br /> | adrp_self =&gt; 0x4007e0<br /> | adrp_self() =&gt; 0xffffffffff7e0<br /> | NOT EQUAL<br /> <br /> After this patch, the ADRP is correctly recognized and simulated:<br /> <br /> | # ./adrp-self<br /> | adrp_self =&gt; 0x4007e0<br /> | adrp_self() =&gt; 0x4007e0<br /> | EQUAL<br /> | #<br /> | # echo &amp;#39;p /root/adrp-self:0x007e0&amp;#39; &gt; /sys/kernel/tracing/uprobe_events<br /> | # echo 1 &gt; /sys/kernel/tracing/events/uprobes/enable<br /> | # ./adrp-self<br /> | adrp_self =&gt; 0x4007e0<br /> | adrp_self() =&gt; 0x4007e0<br /> | EQUAL

Vulnerable products and versions

CPE From Up to
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 4.10 (including) 4.19.323 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 4.20 (including) 5.4.285 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.5 (including) 5.10.229 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.11 (including) 5.15.170 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.16 (including) 6.1.115 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.2 (including) 6.6.58 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.7 (including) 6.11.5 (excluding)
cpe:2.3:o:linux:linux_kernel:6.12:rc1:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.12:rc2:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.12:rc3:*:*:*:*:*:*