Spectre (bounds check bypass / speculative execution leakage)

HIGH
torvalds/linux
Commit: 0c965d2784fb
Affected: LoongArch Linux kernel versions prior to this commit (i.e., before v7.0-rc6 in the LoongArch tree).
2026-04-25 13:58 UTC

Description

Mitigates a Spectre variant 1 style information disclosure in the LoongArch syscall dispatch path. Previously, the syscall number (nr) is used to index into sys_call_table directly after a bounds check; speculative execution could bypass the check and access beyond the end of the syscall table, leaking kernel data. The patch applies array_index_nospec(nr, NR_syscalls) to sanitize the index during speculative execution, preventing leakage.

Proof of Concept

PoC outline (high-level):\n\nBackground: On LoongArch, the syscall dispatch path reads the function pointer from sys_call_table[nr] when a user-space process makes a system call. If an attacker can influence speculative execution to access an out-of-bounds entry before the bounds check is observed, information could be leaked from kernel memory via a side channel.\n\nBefore the fix (vulnerable behavior):\n- Code path: do_syscall(regs) -> if (nr < NR_syscalls) { syscall_fn = sys_call_table[nr]; ... }\n- Speculation: The CPU may speculatively execute the memory load sys_call_table[nr] even when nr >= NR_syscalls, causing a leakage of a byte/word from the underlying memory if coupled with a cache timing side channel.\n\nProof-of-concept (high-level, not a drop-in exploit):\n1) Prepare a user-space probe array cache_probe[256], aligned to cache lines. Each index i maps to a cache line that encodes the value i.\n2) Train the branch predictor to believe nr < NR_syscalls for a specific out-of-range nr, by issuing many legitimate syscall invocations with nr slightly below NR_syscalls.\n3) Trigger a syscall with nr set to a crafted out-of-bounds value (nr >= NR_syscalls). While the kernel still guards by a check, speculative execution could access sys_call_table[array_index_nospec(nr, NR_syscalls)], leaking a byte from the kernel memory pointed to by that slot into the cache line corresponding to that byte value.\n4) Use a user-space timing attack (Prime+Probe or Flush+Reload) on cache_probe to read the leaked byte value from the kernel memory address that was speculatively accessed.\n\nPost-fix expectation:\n- The speculation uses array_index_nospec to clamp the index to a safe value during speculative execution, preventing the out-of-bounds access and thus preventing the leakage via the cache side channel.\n\nImportant notes:\n- Implementing a reliable PoC requires a LoongArch environment and kernel with and without the patch; this outline is meant to illustrate the attack surface and the mitigation mechanism.

Commit Details

Author: Greg Kroah-Hartman

Date: 2026-04-22 07:45 UTC

Message:

LoongArch: Add spectre boundry for syscall dispatch table The LoongArch syscall number is directly controlled by userspace, but does not have a array_index_nospec() boundry to prevent access past the syscall function pointer tables. Cc: stable@vger.kernel.org Assisted-by: gkh_clanker_2000 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>

Triage Assessment

Vulnerability Type: Spectre (bounds check / speculative execution leakage)

Confidence: HIGH

Reasoning:

Adds a spectre boundry using array_index_nospec for syscall dispatch, preventing speculative access beyond the syscall table when the syscall number is user-controlled. This mitigates a potential Spectre variant information disclosure/illegal access in the syscall dispatch path.

Verification Assessment

Vulnerability Type: Spectre (bounds check bypass / speculative execution leakage)

Confidence: HIGH

Affected Versions: LoongArch Linux kernel versions prior to this commit (i.e., before v7.0-rc6 in the LoongArch tree).

Code Diff

diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c index 1249d82c1cd0ac..dac435c3274337 100644 --- a/arch/loongarch/kernel/syscall.c +++ b/arch/loongarch/kernel/syscall.c @@ -9,6 +9,7 @@ #include <linux/entry-common.h> #include <linux/errno.h> #include <linux/linkage.h> +#include <linux/nospec.h> #include <linux/objtool.h> #include <linux/randomize_kstack.h> #include <linux/syscalls.h> @@ -74,7 +75,7 @@ void noinstr __no_stack_protector do_syscall(struct pt_regs *regs) add_random_kstack_offset(); if (nr < NR_syscalls) { - syscall_fn = sys_call_table[nr]; + syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)]; regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6], regs->regs[7], regs->regs[8], regs->regs[9]); }
← Back to Alerts View on GitHub →