diff options
| author | Ali Ahmet Memis <ali@iusegentoo.com> | 2026-08-21 01:45:27 +0000 |
|---|---|---|
| committer | Stafford Horne <shorne@gmail.com> | 2026-08-29 07:32:26 +0100 |
| commit | 78004e9a87f240df03e2f73120d291763c32e0a7 (patch) | |
| tree | 137c22dd795222931b9d2bf2501d37cd766741aa /rust/macros/paste.rs | |
| download | linux-stable-78004e9a87f240df03e2f73120d291763c32e0a7.tar.gz linux-stable-78004e9a87f240df03e2f73120d291763c32e0a7.zip | |
openrisc: fix arbitrary kernel memory access via or1k_atomic syscallgrafted
sys_or1k_atomic() (syscall 244 in the "or1k" ABI) takes two user
pointers, v1 and v2, and swaps the words they point to in hand-written
assembly.
l.lwz r29,0(r4)
l.lwz r27,0(r5)
l.sw 0(r4),r27
l.sw 0(r5),r29
The pointers are not checked with access_ok(). The four memory
accesses also have no exception table entries.
A caller passes a kernel address as either pointer, and the syscall
reads from and writes to it directly.
This gives an unprivileged process a kernel read/write primitive. It
overwrites kernel data such as the sys_call_table, gaining code
execution in kernel context.
Check both pointers before entering the critical section. Add fixups
for the four memory accesses so faults on valid but unmapped user
addresses return -EFAULT.
[shorne@gmail.com: fix comment style]
Fixes: 9d02a4283e9c ("OpenRISC: Boot code")
Cc: stable@vger.kernel.org
Signed-off-by: Ali Ahmet Memis <ali@iusegentoo.com>
Signed-off-by: Stafford Horne <shorne@gmail.com>
Diffstat (limited to 'rust/macros/paste.rs')
| -rw-r--r-- | rust/macros/paste.rs | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/rust/macros/paste.rs b/rust/macros/paste.rs new file mode 100644 index 000000000..2181e312a --- /dev/null +++ b/rust/macros/paste.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenTree}; + +fn concat_helper(tokens: &[TokenTree]) -> Vec<(String, Span)> { + let mut tokens = tokens.iter(); + let mut segments = Vec::new(); + let mut span = None; + loop { + match tokens.next() { + None => break, + Some(TokenTree::Literal(lit)) => { + // Allow us to concat string literals by stripping quotes + let mut value = lit.to_string(); + if value.starts_with('"') && value.ends_with('"') { + value.remove(0); + value.pop(); + } + segments.push((value, lit.span())); + } + Some(TokenTree::Ident(ident)) => { + let mut value = ident.to_string(); + if value.starts_with("r#") { + value.replace_range(0..2, ""); + } + segments.push((value, ident.span())); + } + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + let Some(TokenTree::Ident(ident)) = tokens.next() else { + panic!("expected identifier as modifier"); + }; + + let (mut value, sp) = segments.pop().expect("expected identifier before modifier"); + match ident.to_string().as_str() { + // Set the overall span of concatenated token as current span + "span" => { + assert!( + span.is_none(), + "span modifier should only appear at most once" + ); + span = Some(sp); + } + "lower" => value = value.to_lowercase(), + "upper" => value = value.to_uppercase(), + v => panic!("unknown modifier `{v}`"), + }; + segments.push((value, sp)); + } + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::None => { + let tokens = group.stream().into_iter().collect::<Vec<TokenTree>>(); + segments.append(&mut concat_helper(tokens.as_slice())); + } + token => panic!("unexpected token in paste segments: {token:?}"), + }; + } + + segments +} + +fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree { + let segments = concat_helper(tokens); + let pasted: String = segments.into_iter().map(|x| x.0).collect(); + TokenTree::Ident(Ident::new(&pasted, group_span)) +} + +pub(crate) fn expand(tokens: &mut Vec<TokenTree>) { + for token in tokens.iter_mut() { + if let TokenTree::Group(group) = token { + let delimiter = group.delimiter(); + let span = group.span(); + let mut stream: Vec<_> = group.stream().into_iter().collect(); + // Find groups that looks like `[< A B C D >]` + if delimiter == Delimiter::Bracket + && stream.len() >= 3 + && matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<') + && matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>') + { + // Replace the group with concatenated token + *token = concat(&stream[1..stream.len() - 1], span); + } else { + // Recursively expand tokens inside the group + expand(&mut stream); + let mut group = Group::new(delimiter, stream.into_iter().collect()); + group.set_span(span); + *token = TokenTree::Group(group); + } + } + } + + // Path segments cannot contain invisible delimiter group, so remove them if any. + for i in (0..tokens.len().saturating_sub(3)).rev() { + // Looking for a double colon + if matches!( + (&tokens[i + 1], &tokens[i + 2]), + (TokenTree::Punct(a), TokenTree::Punct(b)) + if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':' + ) { + match &tokens[i + 3] { + TokenTree::Group(group) if group.delimiter() == Delimiter::None => { + tokens.splice(i + 3..i + 4, group.stream()); + } + _ => (), + } + + match &tokens[i] { + TokenTree::Group(group) if group.delimiter() == Delimiter::None => { + tokens.splice(i..i + 1, group.stream()); + } + _ => (), + } + } + } +} |
