Skip to content

Commit 8f09e4b

Browse files
committed
rust: merge Formatter and Buffer
Also fix soundness problems with original `Formatter` when the buffer was `null` and with construction. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 662775d commit 8f09e4b

File tree

5 files changed

+128
-82
lines changed

5 files changed

+128
-82
lines changed

rust/kernel/buffer.rs

Lines changed: 0 additions & 52 deletions
This file was deleted.

rust/kernel/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ pub mod bindings;
4343

4444
#[cfg(CONFIG_ARM_AMBA)]
4545
pub mod amba;
46-
pub mod buffer;
4746
pub mod c_types;
4847
pub mod chrdev;
4948
#[cfg(CONFIG_COMMON_CLK)]

rust/kernel/module_param.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
//!
55
//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
66
7-
use crate::str::CStr;
7+
use crate::error::from_kernel_result;
8+
use crate::str::{CStr, Formatter};
89
use core::fmt::Write;
910

1011
/// Types that can be used for module parameters.
@@ -95,11 +96,11 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
9596
buf: *mut crate::c_types::c_char,
9697
param: *const crate::bindings::kernel_param,
9798
) -> crate::c_types::c_int {
98-
let slice = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, crate::PAGE_SIZE) };
99-
let mut buf = crate::buffer::Buffer::new(slice);
100-
match unsafe { write!(buf, "{}\0", *((*param).__bindgen_anon_1.arg as *mut Self)) } {
101-
Err(_) => crate::error::Error::EINVAL.to_kernel_errno(),
102-
Ok(()) => buf.bytes_written() as crate::c_types::c_int,
99+
from_kernel_result! {
100+
// SAFETY: The C contracts guarantees that the buffer is at least `PAGE_SIZE` bytes.
101+
let mut f = unsafe { Formatter::from_buffer(buf.cast(), crate::PAGE_SIZE) };
102+
unsafe { write!(f, "{}\0", *((*param).__bindgen_anon_1.arg as *mut Self)) }?;
103+
Ok(f.bytes_written().try_into()?)
103104
}
104105
}
105106

rust/kernel/print.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use core::fmt;
1010

1111
use crate::{
1212
c_types::{c_char, c_void},
13-
str::Formatter,
13+
str::RawFormatter,
1414
};
1515

1616
#[cfg(CONFIG_PRINTK)]
@@ -20,13 +20,10 @@ use crate::bindings;
2020
#[no_mangle]
2121
unsafe fn rust_fmt_argument(buf: *mut c_char, end: *mut c_char, ptr: *const c_void) -> *mut c_char {
2222
use fmt::Write;
23-
24-
let mut w = Formatter {
25-
buf: buf as _,
26-
end: end as _,
27-
};
23+
// SAFETY: The C contract guarantees that `buf` is valid if it's less than `end`.
24+
let mut w = unsafe { RawFormatter::from_ptrs(buf.cast(), end.cast()) };
2825
let _ = w.write_fmt(unsafe { *(ptr as *const fmt::Arguments<'_>) });
29-
w.buf as _
26+
w.pos().cast()
3027
}
3128

3229
/// Format strings.

rust/kernel/str.rs

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -374,28 +374,129 @@ mod tests {
374374
}
375375
}
376376

377-
// Use `usize` to use `saturating_*` functions.
378-
pub(crate) struct Formatter {
379-
pub(crate) buf: usize,
380-
pub(crate) end: usize,
377+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
378+
///
379+
/// It does not fail if callers write past the end of the buffer so that they can calculate the
380+
/// size required to fit everything.
381+
///
382+
/// # Invariants
383+
///
384+
/// The memory region between `pos` (inclusive) and `end` (exclusive) is valid for writes if `pos`
385+
/// is less than `end`.
386+
pub(crate) struct RawFormatter {
387+
// Use `usize` to use `saturating_*` functions.
388+
beg: usize,
389+
pos: usize,
390+
end: usize,
381391
}
382392

383-
impl fmt::Write for Formatter {
393+
impl RawFormatter {
394+
/// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
395+
///
396+
/// # Safety
397+
///
398+
/// If `pos` is less than `end`, then the region between `pos` (inclusive) and `end`
399+
/// (exclusive) must be valid for writes for the lifetime of the returned [`RawFormatter`].
400+
pub(crate) unsafe fn from_ptrs(pos: *mut u8, end: *mut u8) -> Self {
401+
// INVARIANT: The safety requierments guarantee the type invariants.
402+
Self {
403+
beg: pos as _,
404+
pos: pos as _,
405+
end: end as _,
406+
}
407+
}
408+
409+
/// Creates a new instance of [`RawFormatter`] with the given buffer.
410+
///
411+
/// # Safety
412+
///
413+
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
414+
/// for the lifetime of the returned [`RawFormatter`].
415+
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
416+
let pos = buf as usize;
417+
// INVARIANT: We ensure that `end` is never less then `buf`, and the safety requirements
418+
// guarantees that the memory region is valid for writes.
419+
Self {
420+
pos,
421+
beg: pos,
422+
end: pos.saturating_add(len),
423+
}
424+
}
425+
426+
/// Returns the current insert position.
427+
///
428+
/// N.B. It may point to invalid memory.
429+
pub(crate) fn pos(&self) -> *mut u8 {
430+
self.pos as _
431+
}
432+
433+
/// Return the number of bytes written to the formatter.
434+
pub(crate) fn bytes_written(&self) -> usize {
435+
self.pos - self.beg
436+
}
437+
}
438+
439+
impl fmt::Write for RawFormatter {
384440
fn write_str(&mut self, s: &str) -> fmt::Result {
385-
// `buf` value after writing `len` bytes. This does not have to be bounded by `end`, but we
441+
// `pos` value after writing `len` bytes. This does not have to be bounded by `end`, but we
386442
// don't want it to wrap around to 0.
387-
let buf_new = self.buf.saturating_add(s.len());
443+
let pos_new = self.pos.saturating_add(s.len());
444+
445+
// Amount that we can copy. `saturating_sub` ensures we get 0 if `pos` goes past `end`.
446+
let len_to_copy = core::cmp::min(pos_new, self.end).saturating_sub(self.pos);
447+
448+
if len_to_copy > 0 {
449+
// SAFETY: If `len_to_copy` is non-zero, then we know `pos` has not gone past `end`
450+
// yet, so it is valid for write per the type invariants.
451+
unsafe {
452+
core::ptr::copy_nonoverlapping(
453+
s.as_bytes().as_ptr(),
454+
self.pos as *mut u8,
455+
len_to_copy,
456+
)
457+
};
458+
}
388459

389-
// Amount that we can copy. `saturating_sub` ensures we get 0 if `buf` goes past `end`.
390-
let len_to_copy = core::cmp::min(buf_new, self.end).saturating_sub(self.buf);
460+
self.pos = pos_new;
461+
Ok(())
462+
}
463+
}
391464

392-
// SAFETY: In any case, `buf` is non-null and properly aligned. If `len_to_copy` is
393-
// non-zero, then we know `buf` has not past `end` yet and so is valid.
394-
unsafe {
395-
core::ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.buf as *mut u8, len_to_copy)
396-
};
465+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
466+
///
467+
/// Fails if callers attempt to write more than will fit in the buffer.
468+
pub(crate) struct Formatter(RawFormatter);
397469

398-
self.buf = buf_new;
399-
Ok(())
470+
impl Formatter {
471+
/// Creates a new instance of [`Formatter`] with the given buffer.
472+
///
473+
/// # Safety
474+
///
475+
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
476+
/// for the lifetime of the returned [`Formatter`].
477+
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
478+
// SAFETY: The safety requirements of this function satisfy those of the callee.
479+
Self(unsafe { RawFormatter::from_buffer(buf, len) })
480+
}
481+
}
482+
483+
impl Deref for Formatter {
484+
type Target = RawFormatter;
485+
486+
fn deref(&self) -> &Self::Target {
487+
&self.0
488+
}
489+
}
490+
491+
impl fmt::Write for Formatter {
492+
fn write_str(&mut self, s: &str) -> fmt::Result {
493+
self.0.write_str(s)?;
494+
495+
// Fail the request if we go past the end of the buffer.
496+
if self.0.pos > self.0.end {
497+
Err(fmt::Error)
498+
} else {
499+
Ok(())
500+
}
400501
}
401502
}

0 commit comments

Comments
 (0)