Skip to content

Commit 51af80d

Browse files
committed
rust: add CBoundedStr
`CBoundedStr<N>` is a `CStr` with length known to be less than `N`. It can be used in cases where a known length limit exists. Signed-off-by: Gary Guo <[email protected]>
1 parent 0de10ea commit 51af80d

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub mod user_ptr;
7070
pub use c_str_check;
7171

7272
pub use crate::error::{Error, KernelResult};
73-
pub use crate::types::{CStr, Mode};
73+
pub use crate::types::{CBoundedStr, CStr, Mode};
7474

7575
/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
7676
///

rust/kernel/types.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
//!
55
//! C header: [`include/linux/types.h`](../../../../include/linux/types.h)
66
7+
use core::ops::Deref;
8+
use link_time_panic::link_time_assert;
9+
710
use crate::bindings;
811
use crate::c_types;
912

@@ -123,6 +126,88 @@ impl CStr {
123126
}
124127
}
125128

129+
/// A `NUL`-terminated string that is guaranteed to be shorter than a given
130+
/// length. This type is useful because C-side usually impose maximum length
131+
/// on types.
132+
///
133+
/// The size parameter `N` represent the maximum number of bytes including NUL.
134+
/// This implies that even though `CBoundedStr<0>` is a well-formed type it cannot
135+
/// be safely created.
136+
#[repr(transparent)]
137+
pub struct CBoundedStr<const N: usize>(CStr);
138+
139+
impl<const N: usize> CBoundedStr<N> {
140+
/// Creates a [`CBoundedStr`] form a [`CStr`].
141+
///
142+
/// The provided `CStr` must be shorten than `N`.
143+
#[inline]
144+
pub fn from_c_str(c_str: &CStr) -> Result<&Self, CStrConvertError> {
145+
if c_str.0.len() > N {
146+
return Err(CStrConvertError::BoundExceeded);
147+
}
148+
149+
// SAFETY: We just checked that all properties hold.
150+
Ok(unsafe { Self::from_c_str_unchecked(c_str) })
151+
}
152+
153+
/// Creates a [`CBoundedStr`] form a [`CStr`] without performing any sanity
154+
/// checks.
155+
///
156+
/// # Safety
157+
///
158+
/// The provided CStr must be shorten than `N`.
159+
#[inline]
160+
pub const unsafe fn from_c_str_unchecked(c_str: &CStr) -> &Self {
161+
&*(c_str as *const CStr as *const Self)
162+
}
163+
164+
/// Creates a [`CBoundedStr`] form a `[u8]`.
165+
///
166+
/// The provided slice must be nul-terminated, does not contain any
167+
/// interior nul bytes and be shorten than `N`.
168+
#[inline]
169+
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError> {
170+
Self::from_c_str(CStr::from_bytes_with_nul(bytes)?)
171+
}
172+
173+
/// Creates a [`CBoundedStr`] form a `[u8]` without performing any sanity
174+
/// checks.
175+
///
176+
/// # Safety
177+
///
178+
/// The provided slice must be nul-terminated, does not contain any
179+
/// interior nul bytes and be shorten than `N`.
180+
#[inline]
181+
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self {
182+
Self::from_c_str_unchecked(CStr::from_bytes_with_nul_unchecked(bytes))
183+
}
184+
185+
/// Expand the string a c_char array, filling remaining bytes with zero.
186+
///
187+
/// `M` must be no less than the bound `N`.
188+
pub const fn expand_into_char_array<const M: usize>(&self) -> [c_types::c_char; M] {
189+
link_time_assert!(
190+
N <= M,
191+
"length of char array must be no less than the bound"
192+
);
193+
let mut ret: [c_types::c_char; M] = [0; M];
194+
let mut i = 0;
195+
while i < self.0 .0.len() {
196+
ret[i] = self.0 .0[i] as _;
197+
i += 1;
198+
}
199+
ret
200+
}
201+
}
202+
203+
impl<const N: usize> Deref for CBoundedStr<N> {
204+
type Target = CStr;
205+
206+
fn deref(&self) -> &Self::Target {
207+
&self.0
208+
}
209+
}
210+
126211
/// Creates a new `CStr` from a string literal.
127212
///
128213
/// The string literal should not contain any `NUL` bytes.
@@ -142,3 +227,33 @@ macro_rules! c_str {
142227
C
143228
}};
144229
}
230+
231+
/// Creates a new `CBoundedStr` from a string literal.
232+
///
233+
/// The string literal should not contain any `NUL` bytes, and its length with NUL should not
234+
/// exceed the bound supplied.
235+
///
236+
/// # Examples
237+
///
238+
/// ```rust,no_run
239+
/// const MY_CSTR: &'static CBoundedStr<100> = c_bounded_str!("My awesome CStr!");
240+
/// ```
241+
///
242+
/// ```rust,compile_fail
243+
/// // shouldn't compile as the string is longer than the specified bound.
244+
/// const MY_CSTR: &'static CBoundedStr<10> = c_bounded_str!("My awesome CStr!");
245+
/// ```
246+
#[macro_export]
247+
macro_rules! c_bounded_str {
248+
($bound:expr, $str:literal) => {{
249+
const C: &'static $crate::CBoundedStr<$bound> = {
250+
let s = $crate::c_str_check::append_nul!($str);
251+
if s.len() > $bound {
252+
// NOPANIC: This is a const panic.
253+
panic!("bound exceeded");
254+
}
255+
unsafe { $crate::CBoundedStr::<$bound>::from_bytes_with_nul_unchecked(s) }
256+
};
257+
C
258+
}};
259+
}

0 commit comments

Comments
 (0)