Skip to content

Commit d26d32a

Browse files
hoshinolinajannau
authored andcommitted
*RFL import: kernel::user_ptr
Commit reference: 3dfc5eb
1 parent 5d6bb62 commit d26d32a

3 files changed

Lines changed: 181 additions & 0 deletions

File tree

rust/helpers/uaccess.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
#include <linux/uaccess.h>
44

5+
unsigned long rust_helper_clear_user(void __user *to, unsigned long n)
6+
{
7+
return clear_user(to, n);
8+
}
9+
510
unsigned long rust_helper_copy_from_user(void *to, const void __user *from,
611
unsigned long n)
712
{

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub mod time;
7979
pub mod transmute;
8080
pub mod types;
8181
pub mod uaccess;
82+
pub mod user_ptr;
8283
pub mod workqueue;
8384
pub mod xarray;
8485

rust/kernel/user_ptr.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! User pointers.
4+
//!
5+
//! C header: [`include/linux/uaccess.h`](../../../../include/linux/uaccess.h)
6+
7+
use crate::{
8+
bindings,
9+
error::code::*,
10+
error::Result,
11+
io_buffer::{IoBufferReader, IoBufferWriter},
12+
};
13+
use alloc::vec::Vec;
14+
15+
/// A reference to an area in userspace memory, which can be either
16+
/// read-only or read-write.
17+
///
18+
/// All methods on this struct are safe: invalid pointers return
19+
/// `EFAULT`. Concurrent access, *including data races to/from userspace
20+
/// memory*, is permitted, because fundamentally another userspace
21+
/// thread/process could always be modifying memory at the same time
22+
/// (in the same way that userspace Rust's [`std::io`] permits data races
23+
/// with the contents of files on disk). In the presence of a race, the
24+
/// exact byte values read/written are unspecified but the operation is
25+
/// well-defined. Kernelspace code should validate its copy of data
26+
/// after completing a read, and not expect that multiple reads of the
27+
/// same address will return the same value.
28+
///
29+
/// All APIs enforce the invariant that a given byte of memory from userspace
30+
/// may only be read once. By preventing double-fetches we avoid TOCTOU
31+
/// vulnerabilities. This is accomplished by taking `self` by value to prevent
32+
/// obtaining multiple readers on a given [`UserSlicePtr`], and the readers
33+
/// only permitting forward reads.
34+
///
35+
/// Constructing a [`UserSlicePtr`] performs no checks on the provided
36+
/// address and length, it can safely be constructed inside a kernel thread
37+
/// with no current userspace process. Reads and writes wrap the kernel APIs
38+
/// `copy_from_user` and `copy_to_user`, which check the memory map of the
39+
/// current process and enforce that the address range is within the user
40+
/// range (no additional calls to `access_ok` are needed).
41+
///
42+
/// [`std::io`]: https://doc.rust-lang.org/std/io/index.html
43+
pub struct UserSlicePtr(*mut core::ffi::c_void, usize);
44+
45+
impl UserSlicePtr {
46+
/// Constructs a user slice from a raw pointer and a length in bytes.
47+
///
48+
/// # Safety
49+
///
50+
/// Callers must be careful to avoid time-of-check-time-of-use
51+
/// (TOCTOU) issues. The simplest way is to create a single instance of
52+
/// [`UserSlicePtr`] per user memory block as it reads each byte at
53+
/// most once.
54+
pub unsafe fn new(ptr: *mut core::ffi::c_void, length: usize) -> Self {
55+
UserSlicePtr(ptr, length)
56+
}
57+
58+
/// Reads the entirety of the user slice.
59+
///
60+
/// Returns `EFAULT` if the address does not currently point to
61+
/// mapped, readable memory.
62+
pub fn read_all(self) -> Result<Vec<u8>> {
63+
self.reader().read_all()
64+
}
65+
66+
/// Constructs a [`UserSlicePtrReader`].
67+
pub fn reader(self) -> UserSlicePtrReader {
68+
UserSlicePtrReader(self.0, self.1)
69+
}
70+
71+
/// Writes the provided slice into the user slice.
72+
///
73+
/// Returns `EFAULT` if the address does not currently point to
74+
/// mapped, writable memory (in which case some data from before the
75+
/// fault may be written), or `data` is larger than the user slice
76+
/// (in which case no data is written).
77+
pub fn write_all(self, data: &[u8]) -> Result {
78+
self.writer().write_slice(data)
79+
}
80+
81+
/// Constructs a [`UserSlicePtrWriter`].
82+
pub fn writer(self) -> UserSlicePtrWriter {
83+
UserSlicePtrWriter(self.0, self.1)
84+
}
85+
86+
/// Constructs both a [`UserSlicePtrReader`] and a [`UserSlicePtrWriter`].
87+
pub fn reader_writer(self) -> (UserSlicePtrReader, UserSlicePtrWriter) {
88+
(
89+
UserSlicePtrReader(self.0, self.1),
90+
UserSlicePtrWriter(self.0, self.1),
91+
)
92+
}
93+
}
94+
95+
/// A reader for [`UserSlicePtr`].
96+
///
97+
/// Used to incrementally read from the user slice.
98+
pub struct UserSlicePtrReader(*mut core::ffi::c_void, usize);
99+
100+
impl IoBufferReader for UserSlicePtrReader {
101+
/// Returns the number of bytes left to be read from this.
102+
///
103+
/// Note that even reading less than this number of bytes may fail.
104+
fn len(&self) -> usize {
105+
self.1
106+
}
107+
108+
/// Reads raw data from the user slice into a raw kernel buffer.
109+
///
110+
/// # Safety
111+
///
112+
/// The output buffer must be valid.
113+
unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result {
114+
if len > self.1 || len > u32::MAX as usize {
115+
return Err(EFAULT);
116+
}
117+
let res = unsafe { bindings::copy_from_user(out as _, self.0, len as _) };
118+
if res != 0 {
119+
return Err(EFAULT);
120+
}
121+
// Since this is not a pointer to a valid object in our program,
122+
// we cannot use `add`, which has C-style rules for defined
123+
// behavior.
124+
self.0 = self.0.wrapping_add(len);
125+
self.1 -= len;
126+
Ok(())
127+
}
128+
}
129+
130+
/// A writer for [`UserSlicePtr`].
131+
///
132+
/// Used to incrementally write into the user slice.
133+
pub struct UserSlicePtrWriter(*mut core::ffi::c_void, usize);
134+
135+
impl IoBufferWriter for UserSlicePtrWriter {
136+
fn len(&self) -> usize {
137+
self.1
138+
}
139+
140+
fn clear(&mut self, mut len: usize) -> Result {
141+
let mut ret = Ok(());
142+
if len > self.1 {
143+
ret = Err(EFAULT);
144+
len = self.1;
145+
}
146+
147+
// SAFETY: The buffer will be validated by `clear_user`. We ensure that `len` is within
148+
// bounds in the check above.
149+
let left = unsafe { bindings::clear_user(self.0, len as _) } as usize;
150+
if left != 0 {
151+
ret = Err(EFAULT);
152+
len -= left;
153+
}
154+
155+
self.0 = self.0.wrapping_add(len);
156+
self.1 -= len;
157+
ret
158+
}
159+
160+
unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result {
161+
if len > self.1 || len > u32::MAX as usize {
162+
return Err(EFAULT);
163+
}
164+
let res = unsafe { bindings::copy_to_user(self.0, data as _, len as _) };
165+
if res != 0 {
166+
return Err(EFAULT);
167+
}
168+
// Since this is not a pointer to a valid object in our program,
169+
// we cannot use `add`, which has C-style rules for defined
170+
// behavior.
171+
self.0 = self.0.wrapping_add(len);
172+
self.1 -= len;
173+
Ok(())
174+
}
175+
}

0 commit comments

Comments
 (0)