summaryrefslogtreecommitdiffstats
path: root/rust/syn/scan_expr.rs
diff options
context:
space:
mode:
authorXie Yuanbin <xieyuanbin1@huawei.com>2026-07-28 03:16:42 +0100
committerRussell King <rmk+kernel@armlinux.org.uk>2026-08-05 16:15:51 +0100
commit1039bffd6ae9c75b42b7d148d6c1106134107b66 (patch)
treee8ee4ac80f87e2ad1b6b0c877eccbb62b0915c02 /rust/syn/scan_expr.rs
downloadlinux-stable-1039bffd6ae9c75b42b7d148d6c1106134107b66.tar.gz
linux-stable-1039bffd6ae9c75b42b7d148d6c1106134107b66.zip
ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faultsgrafted
When CONFIG_DEBUG_USER=y, and cmdline "user_debug=31" is set, a user fault may trigger show_pte() without any lock. If another thread in the same process concurrently calls munmap(), the page table pages may be freed while show_pte() is still traversing them, causing a use-after-free in show_pte(). If CONFIG_ARM_LPAE=y, this may cause a kernel panic if the pages table of PMD are freed when show_pte() is running. Acquire mmap_write_lock() around show_pte() for user faults to fix the contention. For user faults, additionally restrict that show_pte() is called only when the addr is a user-space address (addr < TASK_SIZE). This is because the lock of tsk->mm only protects the virtual memory of user address space, furthermore, dumping the page tables of a kernel-space address for user faults is unnecessary and may have security implications. Keep everything unchanged for kernel faults, because the kernel is already in the "oops" state, acquiring a lock may risk a deadlock. Co-developed-by: Qi Xi <xiqi2@huawei.com> Fixes: 6d021b724481 ("ARM: dump pgd, pmd and pte states on unhandled data abort faults") Link: https://lore.kernel.org/20260716014022.2823-1-xieyuanbin1@huawei.com Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org> Reviewed-by: Linus Walleij <linusw@kernel.org> Signed-off-by: Qi Xi <xiqi2@huawei.com> Signed-off-by: Xie Yuanbin <xieyuanbin1@huawei.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'rust/syn/scan_expr.rs')
-rw-r--r--rust/syn/scan_expr.rs267
1 files changed, 267 insertions, 0 deletions
diff --git a/rust/syn/scan_expr.rs b/rust/syn/scan_expr.rs
new file mode 100644
index 000000000..a5fd1ddc1
--- /dev/null
+++ b/rust/syn/scan_expr.rs
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+use self::{Action::*, Input::*};
+use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
+use syn::parse::{ParseStream, Result};
+use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type};
+
+enum Input {
+ Keyword(&'static str),
+ Punct(&'static str),
+ ConsumeAny,
+ ConsumeBinOp,
+ ConsumeBrace,
+ ConsumeDelimiter,
+ ConsumeIdent,
+ ConsumeLifetime,
+ ConsumeLiteral,
+ ConsumeNestedBrace,
+ ExpectPath,
+ ExpectTurbofish,
+ ExpectType,
+ CanBeginExpr,
+ Otherwise,
+ Empty,
+}
+
+enum Action {
+ SetState(&'static [(Input, Action)]),
+ IncDepth,
+ DecDepth,
+ Finish,
+}
+
+static INIT: [(Input, Action); 28] = [
+ (ConsumeDelimiter, SetState(&POSTFIX)),
+ (Keyword("async"), SetState(&ASYNC)),
+ (Keyword("break"), SetState(&BREAK_LABEL)),
+ (Keyword("const"), SetState(&CONST)),
+ (Keyword("continue"), SetState(&CONTINUE)),
+ (Keyword("for"), SetState(&FOR)),
+ (Keyword("if"), IncDepth),
+ (Keyword("let"), SetState(&PATTERN)),
+ (Keyword("loop"), SetState(&BLOCK)),
+ (Keyword("match"), IncDepth),
+ (Keyword("move"), SetState(&CLOSURE)),
+ (Keyword("return"), SetState(&RETURN)),
+ (Keyword("static"), SetState(&CLOSURE)),
+ (Keyword("unsafe"), SetState(&BLOCK)),
+ (Keyword("while"), IncDepth),
+ (Keyword("yield"), SetState(&RETURN)),
+ (Keyword("_"), SetState(&POSTFIX)),
+ (Punct("!"), SetState(&INIT)),
+ (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])),
+ (Punct("&"), SetState(&REFERENCE)),
+ (Punct("*"), SetState(&INIT)),
+ (Punct("-"), SetState(&INIT)),
+ (Punct("..="), SetState(&INIT)),
+ (Punct(".."), SetState(&RANGE)),
+ (Punct("|"), SetState(&CLOSURE_ARGS)),
+ (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])),
+ (ConsumeLiteral, SetState(&POSTFIX)),
+ (ExpectPath, SetState(&PATH)),
+];
+
+static POSTFIX: [(Input, Action); 10] = [
+ (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])),
+ (Punct("..="), SetState(&INIT)),
+ (Punct(".."), SetState(&RANGE)),
+ (Punct("."), SetState(&DOT)),
+ (Punct("?"), SetState(&POSTFIX)),
+ (ConsumeBinOp, SetState(&INIT)),
+ (Punct("="), SetState(&INIT)),
+ (ConsumeNestedBrace, SetState(&IF_THEN)),
+ (ConsumeDelimiter, SetState(&POSTFIX)),
+ (Empty, Finish),
+];
+
+static ASYNC: [(Input, Action); 3] = [
+ (Keyword("move"), SetState(&ASYNC)),
+ (Punct("|"), SetState(&CLOSURE_ARGS)),
+ (ConsumeBrace, SetState(&POSTFIX)),
+];
+
+static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))];
+
+static BREAK_LABEL: [(Input, Action); 2] = [
+ (ConsumeLifetime, SetState(&BREAK_VALUE)),
+ (Otherwise, SetState(&BREAK_VALUE)),
+];
+
+static BREAK_VALUE: [(Input, Action); 3] = [
+ (ConsumeNestedBrace, SetState(&IF_THEN)),
+ (CanBeginExpr, SetState(&INIT)),
+ (Otherwise, SetState(&POSTFIX)),
+];
+
+static CLOSURE: [(Input, Action); 7] = [
+ (Keyword("async"), SetState(&CLOSURE)),
+ (Keyword("move"), SetState(&CLOSURE)),
+ (Punct(","), SetState(&CLOSURE)),
+ (Punct(">"), SetState(&CLOSURE)),
+ (Punct("|"), SetState(&CLOSURE_ARGS)),
+ (ConsumeLifetime, SetState(&CLOSURE)),
+ (ConsumeIdent, SetState(&CLOSURE)),
+];
+
+static CLOSURE_ARGS: [(Input, Action); 2] = [
+ (Punct("|"), SetState(&CLOSURE_RET)),
+ (ConsumeAny, SetState(&CLOSURE_ARGS)),
+];
+
+static CLOSURE_RET: [(Input, Action); 2] = [
+ (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])),
+ (Otherwise, SetState(&INIT)),
+];
+
+static CONST: [(Input, Action); 2] = [
+ (Punct("|"), SetState(&CLOSURE_ARGS)),
+ (ConsumeBrace, SetState(&POSTFIX)),
+];
+
+static CONTINUE: [(Input, Action); 2] = [
+ (ConsumeLifetime, SetState(&POSTFIX)),
+ (Otherwise, SetState(&POSTFIX)),
+];
+
+static DOT: [(Input, Action); 3] = [
+ (Keyword("await"), SetState(&POSTFIX)),
+ (ConsumeIdent, SetState(&METHOD)),
+ (ConsumeLiteral, SetState(&POSTFIX)),
+];
+
+static FOR: [(Input, Action); 2] = [
+ (Punct("<"), SetState(&CLOSURE)),
+ (Otherwise, SetState(&PATTERN)),
+];
+
+static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)];
+static IF_THEN: [(Input, Action); 2] =
+ [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)];
+
+static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))];
+
+static PATH: [(Input, Action); 4] = [
+ (Punct("!="), SetState(&INIT)),
+ (Punct("!"), SetState(&INIT)),
+ (ConsumeNestedBrace, SetState(&IF_THEN)),
+ (Otherwise, SetState(&POSTFIX)),
+];
+
+static PATTERN: [(Input, Action); 15] = [
+ (ConsumeDelimiter, SetState(&PATTERN)),
+ (Keyword("box"), SetState(&PATTERN)),
+ (Keyword("in"), IncDepth),
+ (Keyword("mut"), SetState(&PATTERN)),
+ (Keyword("ref"), SetState(&PATTERN)),
+ (Keyword("_"), SetState(&PATTERN)),
+ (Punct("!"), SetState(&PATTERN)),
+ (Punct("&"), SetState(&PATTERN)),
+ (Punct("..="), SetState(&PATTERN)),
+ (Punct(".."), SetState(&PATTERN)),
+ (Punct("="), SetState(&INIT)),
+ (Punct("@"), SetState(&PATTERN)),
+ (Punct("|"), SetState(&PATTERN)),
+ (ConsumeLiteral, SetState(&PATTERN)),
+ (ExpectPath, SetState(&PATTERN)),
+];
+
+static RANGE: [(Input, Action); 6] = [
+ (Punct("..="), SetState(&INIT)),
+ (Punct(".."), SetState(&RANGE)),
+ (Punct("."), SetState(&DOT)),
+ (ConsumeNestedBrace, SetState(&IF_THEN)),
+ (Empty, Finish),
+ (Otherwise, SetState(&INIT)),
+];
+
+static RAW: [(Input, Action); 3] = [
+ (Keyword("const"), SetState(&INIT)),
+ (Keyword("mut"), SetState(&INIT)),
+ (Otherwise, SetState(&POSTFIX)),
+];
+
+static REFERENCE: [(Input, Action); 3] = [
+ (Keyword("mut"), SetState(&INIT)),
+ (Keyword("raw"), SetState(&RAW)),
+ (Otherwise, SetState(&INIT)),
+];
+
+static RETURN: [(Input, Action); 2] = [
+ (CanBeginExpr, SetState(&INIT)),
+ (Otherwise, SetState(&POSTFIX)),
+];
+
+pub(crate) fn scan_expr(input: ParseStream) -> Result<()> {
+ let mut state = INIT.as_slice();
+ let mut depth = 0usize;
+ 'table: loop {
+ for rule in state {
+ if match rule.0 {
+ Input::Keyword(expected) => input.step(|cursor| match cursor.ident() {
+ Some((ident, rest)) if ident == expected => Ok((true, rest)),
+ _ => Ok((false, *cursor)),
+ })?,
+ Input::Punct(expected) => input.step(|cursor| {
+ let begin = *cursor;
+ let mut cursor = begin;
+ for (i, ch) in expected.chars().enumerate() {
+ match cursor.punct() {
+ Some((punct, _)) if punct.as_char() != ch => break,
+ Some((_, rest)) if i == expected.len() - 1 => {
+ return Ok((true, rest));
+ }
+ Some((punct, rest)) if punct.spacing() == Spacing::Joint => {
+ cursor = rest;
+ }
+ _ => break,
+ }
+ }
+ Ok((false, begin))
+ })?,
+ Input::ConsumeAny => input.parse::<Option<TokenTree>>()?.is_some(),
+ Input::ConsumeBinOp => input.parse::<BinOp>().is_ok(),
+ Input::ConsumeBrace | Input::ConsumeNestedBrace => {
+ (matches!(rule.0, Input::ConsumeBrace) || depth > 0)
+ && input.step(|cursor| match cursor.group(Delimiter::Brace) {
+ Some((_inside, _span, rest)) => Ok((true, rest)),
+ None => Ok((false, *cursor)),
+ })?
+ }
+ Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() {
+ Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)),
+ None => Ok((false, *cursor)),
+ })?,
+ Input::ConsumeIdent => input.parse::<Option<Ident>>()?.is_some(),
+ Input::ConsumeLifetime => input.parse::<Option<Lifetime>>()?.is_some(),
+ Input::ConsumeLiteral => input.parse::<Option<Lit>>()?.is_some(),
+ Input::ExpectPath => {
+ input.parse::<ExprPath>()?;
+ true
+ }
+ Input::ExpectTurbofish => {
+ if input.peek(Token![::]) {
+ input.parse::<AngleBracketedGenericArguments>()?;
+ }
+ true
+ }
+ Input::ExpectType => {
+ Type::without_plus(input)?;
+ true
+ }
+ Input::CanBeginExpr => Expr::peek(input),
+ Input::Otherwise => true,
+ Input::Empty => input.is_empty() || input.peek(Token![,]),
+ } {
+ state = match rule.1 {
+ Action::SetState(next) => next,
+ Action::IncDepth => (depth += 1, &INIT).1,
+ Action::DecDepth => (depth -= 1, &POSTFIX).1,
+ Action::Finish => return if depth == 0 { Ok(()) } else { break },
+ };
+ continue 'table;
+ }
+ }
+ return Err(input.error("unsupported expression"));
+ }
+}