atmos/kernel/usercopy.rs
1//! usercopy.rs - ユーザ空間メモリへの安全なアクセス層
2//!
3//! EL0 (アプリケーション) から渡されたポインタをカーネル (EL1) が参照する際の
4//! 事前条件チェックを一元化するモジュール。システムコール実装は生ポインタを
5//! 直接 deref せず、必ずこのモジュールの関数を経由すること。
6//!
7//! # 検証する事前条件
8//!
9//! 1. **アドレス範囲**: ユーザ空間として妥当な恒等マップ RAM 領域
10//! (`USER_RAM_START..USER_RAM_END`) に完全に収まっていること。
11//! カーネル領域 (低位アドレス) や MMIO 領域 (0x3C00_0000 以上) を指す
12//! ポインタは拒否する。
13//! 2. **オーバーフロー**: `ptr + len` がラップアラウンドしないこと。
14//! 3. **アライメント**: 型 `T` の自然アライメントを満たすこと
15//! (未満たしの参照作成は Rust では未定義動作)。
16//! 4. **長さ上限**: 呼び出し側が指定する上限を超えないこと。
17//!
18//! これらのチェックにより、アプリケーションがどんな引数を渡しても
19//! カーネルがクラッシュ・メモリ破壊しないことを保証する。
20#![allow(dead_code)]
21// EL0 境界モジュール: パニックにつながる操作を deny で強制排除
22#![deny(
23 clippy::unwrap_used,
24 clippy::expect_used,
25 clippy::panic,
26 clippy::indexing_slicing
27)]
28
29/// ユーザ空間として許可する RAM 領域の下限 (カーネルイメージ領域を除外)
30pub const USER_RAM_START: usize = 0x20_0000;
31/// ユーザ空間として許可する RAM 領域の上限 (MMIO 領域 0x3C00_0000 の手前まで)
32pub const USER_RAM_END: usize = 0x3C00_0000;
33
34/// `[ptr, ptr+len_bytes)` がユーザ RAM 領域内に完全に収まるか検査する。
35/// `len_bytes == 0` または範囲外・ラップアラウンドする場合は `false`。
36pub fn user_range_ok(ptr: usize, len_bytes: usize) -> bool {
37 if len_bytes == 0 {
38 return false;
39 }
40 let end = match ptr.checked_add(len_bytes) {
41 Some(e) => e,
42 None => return false,
43 };
44 ptr >= USER_RAM_START && end <= USER_RAM_END
45}
46
47/// 型 `T` を `count` 個読むためのポインタとして妥当か検査する
48/// (範囲 + アライメント + 要素数オーバーフロー)。
49pub fn user_ptr_ok<T>(ptr: usize, count: usize) -> bool {
50 if !ptr.is_multiple_of(core::mem::align_of::<T>()) {
51 return false;
52 }
53 let len_bytes = match count.checked_mul(core::mem::size_of::<T>()) {
54 Some(n) => n,
55 None => return false,
56 };
57 user_range_ok(ptr, len_bytes)
58}
59
60/// ユーザ空間から型 `T` のスライスを借用する。
61/// 事前条件 (範囲・アライメント) を満たさない場合は `None`。
62///
63/// # Safety 上の注意
64/// 返るスライスはユーザメモリを直接参照するため、保持したまま
65/// スケジューラに制御を戻してはならない (コピーしてから使うこと)。
66pub fn user_slice<'a, T>(ptr: usize, count: usize) -> Option<&'a [T]> {
67 if !user_ptr_ok::<T>(ptr, count) {
68 return None;
69 }
70 Some(unsafe { core::slice::from_raw_parts(ptr as *const T, count) })
71}
72
73/// ユーザ空間のバイト列を UTF-8 文字列として検証付きで借用する。
74/// 範囲外・非 UTF-8 の場合は `None`。`max_len` を超える長さは拒否する。
75/// `len == 0` はメモリを読まないため常に空文字列として許可する。
76pub fn user_str<'a>(ptr: usize, len: usize, max_len: usize) -> Option<&'a str> {
77 if len > max_len {
78 return None;
79 }
80 if len == 0 {
81 return Some("");
82 }
83 let bytes = user_slice::<u8>(ptr, len)?;
84 core::str::from_utf8(bytes).ok()
85}
86
87/// ユーザ空間へ型 `T` の値を 1 個書き込む。
88/// 事前条件を満たさない場合は `false` を返し、何も書き込まない。
89pub fn write_user<T: Copy>(ptr: usize, value: T) -> bool {
90 if !user_ptr_ok::<T>(ptr, 1) {
91 return false;
92 }
93 unsafe { (ptr as *mut T).write_volatile(value) };
94 true
95}