RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / import / kernel-user / cowtest.rs
3835 bytes ยท raw
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
//! cowtest -- checks that fork really gives the child its *own* memory.
//!
//! fork doesn't copy anything up front: parent and child share every page,
//! read-only, and the first write to a page makes the copy (copy-on-write).
//! This checks that the copies are made at the right moments: writes in the
//! child don't show in the parent and vice versa -- for global data, the
//! heap and the stack -- while MAP_SHARED memory *is* shared.
#![no_std]
#![no_main]

extern crate alloc;

#[macro_use]
#[path = "ulib.rs"]
mod ulib;

use alloc::vec;
use ulib::*;

/// 64 KiB of global data (16 pages, in .bss).
static mut GLOBAL: [u8; 65536] = [0; 65536];

fn global() -> &'static mut [u8; 65536] {
    unsafe { &mut *core::ptr::addr_of_mut!(GLOBAL) }
}

fn all(buf: &[u8], value: u8) -> bool {
    buf.iter().all(|&b| b == value)
}

fn main() -> i64 {
    let mut c = Checks::new("cowtest");
    global().fill(1);
    let mut heap = vec![2u8; 100_000];
    let mut stack = [3u8; 2000];

    // 1. The child writes; the parent must not see it.
    let pid = fork();
    if pid == 0 {
        let before = all(global(), 1) && all(&heap, 2) && all(&stack, 3);
        global().fill(0xc1);
        heap.fill(0xc2);
        stack.fill(0xc3);
        let after = all(global(), 0xc1) && all(&heap, 0xc2) && all(core::hint::black_box(&stack), 0xc3);
        exit(if before && after { 0 } else { 1 });
    }
    c.check(wait(pid) == 0, "the child starts with the parent's data and can change its copy");
    c.check(all(global(), 1), "the child's writes to global data stay in the child");
    c.check(all(&heap, 2), "... and to the heap");
    c.check(all(core::hint::black_box(&stack), 3), "... and to the stack");

    // 2. The parent writes after fork; the child must keep the old data.
    // A pipe orders things: the child only checks after we wrote.
    let (r, w) = pipe().unwrap_or((-1, -1));
    let pid = fork();
    if pid == 0 {
        close(w);
        let mut b = [0u8];
        read(r, &mut b); // wait for the parent's go
        exit(if all(global(), 1) && all(&heap, 2) { 0 } else { 1 });
    }
    close(r);
    global().fill(0x77);
    heap.fill(0x77);
    write(w, b"!");
    close(w);
    c.check(wait(pid) == 0, "the parent's writes after fork stay in the parent");
    c.check(all(global(), 0x77), "and the parent sees its own writes");

    // 3. A grandchild: three generations share pages, each writes.
    let pid = fork();
    if pid == 0 {
        let pid2 = fork();
        if pid2 == 0 {
            global()[0] = 3;
            exit(if global()[1] == 0x77 { 0 } else { 1 });
        }
        global()[0] = 2;
        let code = wait(pid2);
        exit(if code == 0 && global()[0] == 2 { 0 } else { 1 });
    }
    c.check(wait(pid) == 0 && global()[0] == 0x77, "three generations: each keeps its own copy");

    // 4. MAP_SHARED memory is *not* copied: both see the same pages.
    let shared = mmap(0, 8192, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    let private = mmap(0, 8192, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    c.check(shared > 0 && private > 0, "mmap shared + private anonymous memory");
    let (sp, pp) = (shared as *mut u8, private as *mut u8);
    unsafe {
        sp.write_volatile(10);
        pp.write_volatile(10);
    }
    let pid = fork();
    if pid == 0 {
        unsafe {
            sp.write_volatile(42);
            sp.add(4096).write_volatile(43); // a page nobody touched before fork
            pp.write_volatile(42);
        }
        exit(0);
    }
    wait(pid);
    let (s0, s1, p0) = unsafe { (sp.read_volatile(), sp.add(4096).read_volatile(), pp.read_volatile()) };
    c.check(s0 == 42 && s1 == 43, "MAP_SHARED: the child's writes show up in the parent");
    c.check(p0 == 10, "MAP_PRIVATE: they don't");
    c.done()
}