1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
| #![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(rustbelt::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use rustbelt::errno::Errno;
use rustbelt::fs::file::File;
use rustbelt::process::{self, ExitStatus, SpawnArgs};
use rustbelt::{elf, fs, memory, thread};
entry_point!(main);
fn main(boot_info: &'static BootInfo) -> ! {
rustbelt::init();
rustbelt::init_memory(boot_info);
rustbelt::init_smp(); fs::init();
rustbelt::init_storage();
test_main();
rustbelt::hlt_loop();
}
fn run(name: &str, args: &[&str]) -> (ExitStatus, String) {
let (reader, writer) = fs::pipe::new();
let mut spawn = SpawnArgs::kernel(name);
spawn.args.extend(args.iter().map(|a| String::from(*a)));
spawn.stdio[1] = Arc::new(writer);
let p = process::spawn_path(&alloc::format!("/mnt/disk0/bin/{}", name), spawn).expect("spawn failed");
let mut out = Vec::new();
let mut buf = [0u8; 256];
loop {
match reader.read(&mut buf).unwrap() {
0 => break,
n => out.extend_from_slice(&buf[..n]),
}
}
(process::wait(&p), String::from_utf8_lossy(&out).into_owned())
}
fn counts() -> (usize, usize, usize) {
thread::sleep_ticks(3);
(thread::thread_count(), process::count(), memory::frames_in_use())
}
#[test_case]
fn static_pie_hello() {
let (status, out) = run("piehello", &[]);
assert!(out.starts_with("hello from a static PIE\n"), "output: {}", out);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
}
#[test_case]
fn static_pie_base_is_random() {
let parse = |s: &str| u64::from_str_radix(s.trim().trim_start_matches("0x"), 16).unwrap();
let (s1, a) = run("piehello", &["where"]);
let (s2, b) = run("piehello", &["where"]);
assert_eq!((s1, s2), (ExitStatus::Exited(0), ExitStatus::Exited(0)));
let (a, b) = (parse(&a), parse(&b));
assert_ne!(a, b, "same base twice");
assert_eq!(a % 4096, b % 4096, "main moved within its page");
assert!(a > memory::USER_START && a < rustbelt::mm::MMAP_TOP);
}
#[test_case]
fn static_pie_threads_and_stdio() {
let (status, out) = run("piethreads", &[]);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
let before = counts();
let (status, out) = run("piethreads", &[]);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
assert!(out.contains("piethreads: 0 failed"));
assert_eq!(counts(), before, "piethreads leaked something");
}
#[test_case]
fn dynamic_program_is_enoexec() {
let image = fs::read_all("/mnt/disk0/bin/dynhello").unwrap();
assert_eq!(elf::check(&image), Err(elf::ElfError::NeedsInterpreter));
let err = process::spawn_path("/mnt/disk0/bin/dynhello", SpawnArgs::kernel("dynhello")).err();
assert_eq!(err, Some(Errno::ENOEXEC));
}
#[test_case]
fn exec_files_still_load() {
for name in ["hello", "rawabi", "vdsotest", "exechello", "execthreads"] {
let image = fs::read_all(&alloc::format!("/mnt/disk0/bin/{}", name)).unwrap();
assert_eq!(u16::from_le_bytes([image[16], image[17]]), 2, "{} is not ET_EXEC", name);
assert!(elf::check(&image).is_ok(), "{} doesn't load", name);
}
let pie = fs::read_all("/mnt/disk0/bin/piehello").unwrap();
assert_eq!(u16::from_le_bytes([pie[16], pie[17]]), 3);
let entry = elf::check(&pie).unwrap();
assert!(entry > memory::USER_START + 0x40_0000, "PIE entry {:#x} not biased", entry);
assert_eq!(run("rawabi", &["/tmp"]).0, ExitStatus::Exited(0));
}
#[test_case]
fn stock_static_exec_hello() {
let image = fs::read_all("/mnt/disk0/bin/exechello").unwrap();
assert_eq!(u16::from_le_bytes([image[16], image[17]]), 2, "not ET_EXEC");
assert!(elf::check(&image).unwrap() < 0x50_0000, "not linked at 0x400000");
let before = counts();
let (status, out) = run("exechello", &[]);
assert!(out.starts_with("hello from a plain static executable\n"), "output: {}", out);
assert!(out.contains("exechello: 0 failed"), "output: {}", out);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
assert_eq!(counts(), before, "exechello leaked something");
}
#[test_case]
fn stock_static_exec_threads_and_stdio() {
let image = fs::read_all("/mnt/disk0/bin/execthreads").unwrap();
assert_eq!(u16::from_le_bytes([image[16], image[17]]), 2, "not ET_EXEC");
let (status, out) = run("execthreads", &[]);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
let before = counts();
let (status, out) = run("execthreads", &[]);
assert_eq!(status, ExitStatus::Exited(0), "output: {}", out);
assert!(out.contains("piethreads: 0 failed"));
assert_eq!(counts(), before, "execthreads leaked something");
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
rustbelt::test_panic_handler(info)
}
|