Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/docs/debug/check-mechanisms-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ boot/current 栈,也不覆盖未来可能引入的独立 IRQ stack、exception
或 overflow stack。这个边界与 `plat-dyn` / 非 `plat-dyn` 无直接绑定:
两种平台模式下,动态任务栈都覆盖,borrowed 栈都暂不覆盖。

Linux 的栈保护包含两层不同机制。`STACK_END_MAGIC` 用于检查任务栈底是否
被覆盖,作用与当前 `stack-canary` 接近;`CONFIG_STACKPROTECTOR` /
`CONFIG_STACKPROTECTOR_STRONG` 则依赖编译器在函数栈帧中插入 canary,
函数返回前比较保存值和运行时 guard,失败时调用 `__stack_chk_fail()`。
后者可以发现尚未一路覆盖到任务栈底的函数局部栈溢出,是当前机制尚未覆盖
的方向。

项目后续可参照 Linux 分阶段增强栈帧级保护。第一阶段优先实现跨架构的
全局 guard 方案:通过 opt-in hardening 开关在构建系统中注入
`-Z stack-protector=strong`,并在内核运行时提供 `__stack_chk_guard`
和 `__stack_chk_fail()`。当前 nightly 对项目使用的
`x86_64-unknown-none`、`riscv64gc-unknown-none-elf`、
`aarch64-unknown-none-softfloat`、`loongarch64-unknown-none-softfloat`
四个目标都接受 `-Z stack-protector=strong`,生成对象也统一依赖
`__stack_chk_guard` / `__stack_chk_fail`,因此全局 guard 方案可以作为
四架构共同的最小闭环。第二阶段再评估 Linux 风格 per-task 或 per-cpu
guard:x86_64、riscv64、aarch64 可结合各自 percpu / thread pointer /
系统寄存器约定逐步设计;loongarch64 在 Linux 6.12 中也主要体现为全局
`__stack_chk_guard` 路径,建议放在全局方案稳定后再单独评估。

平台栈边界需要按平台类型区分。静态平台可以使用 linker script 中的
`boot_stack` / `boot_stack_top` 符号作为主 CPU boot stack 的边界;
`plat-dyn` 下这两个符号只是兼容占位,并不表示真实栈空间。`plat-dyn`
Expand All @@ -123,6 +143,9 @@ boot/current 栈,也不覆盖未来可能引入的独立 IRQ stack、exception

- 在更多边界点触发检查,例如任务退出、panic 前诊断或长时间运行的 idle 路径。
- 持续完善动态任务栈 guard page 的 SMP shootdown、跨架构 QEMU 回归和 fault 诊断。
- 增加 opt-in 的编译器栈帧级 stack protector,先采用四架构通用的
全局 `__stack_chk_guard` / `__stack_chk_fail` 方案,再评估 per-task
或 per-cpu guard。
- 后续在 `axmm` 上补 kernel vmap allocator,把 guard page 从额外物理页演进为仅占虚拟地址空间的空洞。
- 在 vmap-style 栈和 stack metadata 稳定后,再评估 borrowed boot/current 栈、secondary boot 栈以及专用 IRQ/exception/overflow 栈的 guard page 接入。
- 完善不同架构和不同平台栈布局的文档,明确 canary 写入位置和误报边界。
Expand Down
1 change: 1 addition & 0 deletions os/StarryOS/kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sg2002-wifi = [
"ax-feat/aic8800-wifi",
]
stack-guard-page = ["ax-feat/stack-guard-page"]
stack-protector = ["ax-feat/stack-protector"]

[dependencies]
ax-feat = { workspace = true, features = [
Expand Down
1 change: 1 addition & 0 deletions os/StarryOS/starryos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ plat-dyn = ["dep:axplat-dyn", "starry-kernel/plat-dyn"]

rknpu = ["ax-driver/rknpu", "starry-kernel/rknpu"]
std-compat = ["starry-kernel/std-compat"]
stack-protector = ["ax-std/stack-protector", "starry-kernel/stack-protector"]
vf2 = ["ax-hal/riscv64-visionfive2"]

[[bin]]
Expand Down
1 change: 1 addition & 0 deletions os/arceos/api/axfeat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ stack-guard-page = [
"ax-task/stack-guard-page",
"ax-runtime/stack-guard-page",
]
stack-protector = ["ax-runtime/stack-protector"]

# File system
fs = [
Expand Down
1 change: 1 addition & 0 deletions os/arceos/api/axfeat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! - `sched-fifo`: Use the FIFO cooperative scheduler.
//! - `sched-rr`: Use the Round-robin preemptive scheduler.
//! - `sched-cfs`: Use the Completely Fair Scheduler (CFS) preemptive scheduler.
//! - `stack-protector`: Enable compiler-inserted stack frame canary checks.
//! - Upperlayer stacks (fs, net, display)
//! - `fs`: Enable file system support.
//! - `net`: Enable networking support.
Expand Down
1 change: 1 addition & 0 deletions os/arceos/modules/axruntime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ paging = ["ax-hal/paging", "dep:ax-mm", "dep:axklib"]
rtc = ["ax-driver/rtc"]
smp = ["ax-hal/smp", "ax-task?/smp"]
stack-guard-page = ["multitask", "paging", "ax-task/stack-guard-page"]
stack-protector = []
tls = ["ax-hal/tls", "ax-task?/tls"]

plat-dyn = [
Expand Down
6 changes: 6 additions & 0 deletions os/arceos/modules/axruntime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ extern crate ax_driver as _;

#[cfg(all(target_os = "none", not(feature = "std-compat"), not(test)))]
mod lang_items;
#[cfg(all(
feature = "stack-protector",
any(target_os = "none", target_env = "musl"),
not(test)
))]
mod stack_protector;

#[cfg(feature = "smp")]
mod mp;
Expand Down
12 changes: 12 additions & 0 deletions os/arceos/modules/axruntime/src/stack_protector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Runtime support for compiler-inserted stack protector checks.

const STACK_CHK_GUARD: usize = 0x57AC_CE11_5A7A_CE11usize;

#[unsafe(no_mangle)]
#[used]
pub static __stack_chk_guard: usize = STACK_CHK_GUARD;

#[unsafe(no_mangle)]
pub extern "C" fn __stack_chk_fail() -> ! {
panic!("stack-protector: kernel stack is corrupted")
}
1 change: 1 addition & 0 deletions os/arceos/ulib/axlibc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ tls = ["alloc", "ax-feat/tls"]
# Multi-task
multitask = ["ax-feat/multitask", "ax-posix-api/multitask"]
lockdep = ["ax-posix-api/lockdep", "ax-feat/lockdep"]
stack-protector = ["ax-feat/stack-protector"]

# File system
fs = ["ax-posix-api/fs", "fd"]
Expand Down
1 change: 1 addition & 0 deletions os/arceos/ulib/axstd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ sched-fifo = ["ax-feat/sched-fifo"]
sched-rr = ["ax-feat/sched-rr"]
sched-cfs = ["ax-feat/sched-cfs"]
stack-guard-page = ["multitask", "paging", "ax-feat/stack-guard-page"]
stack-protector = ["ax-feat/stack-protector"]

# File system
fs = [
Expand Down
1 change: 1 addition & 0 deletions os/axvisor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ sdmmc = [
]
rockchip-pm = ["ax-driver/rockchip-pm"]
serial = []
stack-protector = ["ax-std/stack-protector"]

[dependencies]
spin = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion scripts/axbuild/src/arceos/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ pub(crate) fn load_c_app_cargo_config(request: &ResolvedBuildRequest) -> anyhow:
plat_dyn,
metadata,
);
let rustflags = build::toolchain_rustflags(&build_info.env);
let rustflags = build::toolchain_rustflags_for_features(&build_info.env, &build_info.features);
let args = ArceosBuildInfo::build_cargo_args(&request.target, &rustflags);

build_info.prepare_log_env();
Expand Down
84 changes: 83 additions & 1 deletion scripts/axbuild/src/axvisor/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ fn to_cargo_config(
metadata: &cargo_metadata::Metadata,
) -> anyhow::Result<Cargo> {
config.target = request.target.clone();
let makefile_features = crate::build::makefile_features_from_env();
crate::build::apply_makefile_features_with_metadata(
&mut config.build_info,
&request.package,
&makefile_features,
metadata,
);
let known_platforms = platform_feature_names(metadata);
reject_unsupported_nested_platform_features(&config.build_info.features, &known_platforms)?;
let plat_dyn = config
Expand Down Expand Up @@ -571,12 +578,48 @@ fn load_build_config(request: &ResolvedAxvisorRequest) -> anyhow::Result<LoadedA

#[cfg(test)]
mod tests {
use std::{fs, path::Path};
use std::{
env,
ffi::{OsStr, OsString},
fs,
path::Path,
sync::{LazyLock, Mutex},
};

use tempfile::tempdir;

use super::*;

static ENV_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));

struct TempEnvVar {
key: &'static str,
original: Option<OsString>,
}

impl TempEnvVar {
fn set(key: &'static str, value: impl AsRef<OsStr>) -> Self {
let original = env::var_os(key);
unsafe {
env::set_var(key, value);
}
Self { key, original }
}
}

impl Drop for TempEnvVar {
fn drop(&mut self) {
match self.original.as_ref() {
Some(value) => unsafe {
env::set_var(self.key, value);
},
None => unsafe {
env::remove_var(self.key);
},
}
}
}

fn write_board(axvisor_dir: &Path, name: &str, body: &str) -> PathBuf {
let path = axvisor_dir
.join("configs/board")
Expand Down Expand Up @@ -1095,6 +1138,45 @@ log = "Info"
assert!(!cargo.features.contains(&"ax-hal/x86-pc".to_string()));
}

#[test]
fn load_cargo_config_applies_stack_protector_from_makefile_features() {
let _env_lock = ENV_LOCK.lock().unwrap();
let _features = TempEnvVar::set("FEATURES", "stack-protector");
let root = tempdir().unwrap();
let config_path = root.path().join(".build.toml");
fs::write(
&config_path,
r#"
features = ["ept-level-4", "fs", "vmx"]
log = "Info"
"#,
)
.unwrap();

let cargo = load_cargo_config(&ResolvedAxvisorRequest {
package: AXVISOR_PACKAGE.to_string(),
axvisor_dir: root.path().join("os/axvisor"),
arch: "x86_64".to_string(),
target: "x86_64-unknown-none".to_string(),
plat_dyn: None,
smp: None,
debug: false,
build_info_path: config_path,
qemu_config: None,
uboot_config: None,
vmconfigs: vec![],
})
.unwrap();

assert!(
cargo
.features
.contains(&"ax-std/stack-protector".to_string())
);
let config = fs::read_to_string(cargo.extra_config.unwrap()).unwrap();
assert!(config.contains(r#""-Zstack-protector=strong""#));
}

#[test]
fn load_cargo_config_keeps_loongarch_axvisor_as_elf() {
let root = tempdir().unwrap();
Expand Down
82 changes: 81 additions & 1 deletion scripts/axbuild/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ pub(crate) fn toolchain_rustflags(env: &HashMap<String, String>) -> Vec<String>
flags
}

fn features_enable_stack_protector(features: &[String]) -> bool {
features.iter().any(|feature| {
matches!(
feature.as_str(),
"stack-protector"
| "ax-std/stack-protector"
| "ax-feat/stack-protector"
| "starry-kernel/stack-protector"
)
})
}

pub(crate) fn toolchain_rustflags_for_features(
env: &HashMap<String, String>,
features: &[String],
) -> Vec<String> {
let mut flags = toolchain_rustflags(env);
if features_enable_stack_protector(features) {
flags.push("-Zstack-protector=strong".to_string());
}
flags
}

/// Whether the build config enables target backtrace support (frame pointers / unwind).
///
/// Matches [`toolchain_rustflags`]: `BACKTRACE=y` or `DWARF=y` in `[env]`.
Expand Down Expand Up @@ -225,7 +248,7 @@ impl BuildInfo {
.display()
.to_string(),
);
let rustflags = toolchain_rustflags(&cargo.env);
let rustflags = toolchain_rustflags_for_features(&cargo.env, &cargo.features);
cargo.extra_config = Some(
std_cargo_config_path(&std_target.target_name, &wrapper, plat_dyn, &rustflags)?
.display()
Expand Down Expand Up @@ -1295,6 +1318,7 @@ fn is_known_axstd_feature(feature: &str) -> bool {
| "sched-rr"
| "sched-cfs"
| "stack-guard-page"
| "stack-protector"
| "fs"
| "ext4fs"
| "fatfs"
Expand Down Expand Up @@ -2098,6 +2122,36 @@ log = "Info"
);
}

#[test]
fn toolchain_rustflags_enable_stack_protector_from_features() {
let env = HashMap::from([("BACKTRACE".to_string(), "y".to_string())]);
let features = vec!["ax-std/stack-protector".to_string()];

assert_eq!(
toolchain_rustflags_for_features(&env, &features),
vec![
"-Cforce-frame-pointers=yes".to_string(),
"-Zstack-protector=strong".to_string(),
]
);
}

#[test]
fn stack_protector_feature_detection_accepts_supported_surfaces() {
for feature in [
"stack-protector",
"ax-std/stack-protector",
"ax-feat/stack-protector",
"starry-kernel/stack-protector",
] {
assert!(features_enable_stack_protector(&[feature.to_string()]));
}

assert!(!features_enable_stack_protector(&[
"stack-guard-page".to_string()
]));
}

#[test]
fn std_build_nested_features_are_passed_through_not_enabled_on_app() {
let mut envs = HashMap::new();
Expand Down Expand Up @@ -2552,6 +2606,32 @@ log = "Info"
assert!(config.contains(r#""-Cforce-frame-pointers=yes""#));
}

#[test]
fn std_build_config_enables_stack_protector_from_feature() {
let metadata = repo_metadata();
let info = BuildInfo {
features: vec!["stack-protector".to_string()],
..BuildInfo::default()
};

let cargo = info
.into_prepared_base_cargo_config_with_metadata(
"arceos-helloworld",
"x86_64-unknown-none",
None,
&metadata,
)
.unwrap();

assert!(
cargo
.features
.contains(&"ax-std/stack-protector".to_string())
);
let config = std::fs::read_to_string(cargo.extra_config.unwrap()).unwrap();
assert!(config.contains(r#""-Zstack-protector=strong""#));
}

#[test]
fn std_build_target_maps_arceos_targets_to_linux_musl_by_link_mode() {
let cases = [
Expand Down