RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / tests / unix_sockets.rs
3471 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
//! Stage 18: AF_UNIX sockets and /proc/self. The detailed checks live in
//! user/unixtest.rs (exit code = failed checks); this file runs each
//! group twice and checks nothing is left behind -- threads, processes,
//! memory frames (fds in flight included), and bound names.
#![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 bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use rustbelt::process::{self, ExitStatus, SpawnArgs};
use rustbelt::{fs, memory, net, syscall, thread};

entry_point!(main);

fn main(boot_info: &'static BootInfo) -> ! {
    rustbelt::init();
    rustbelt::init_memory(boot_info);
    rustbelt::init_smp(); // all 4 CPUs run threads
    fs::init();
    rustbelt::init_storage();
    test_main();
    rustbelt::hlt_loop();
}

fn run(args: &[&str]) -> ExitStatus {
    let mut spawn = SpawnArgs::kernel("unixtest");
    spawn.args.extend(args.iter().map(|a| String::from(*a)));
    let p = process::spawn_path("/mnt/disk0/bin/unixtest", spawn).expect("spawn failed");
    process::wait(&p)
}

fn counts() -> (usize, usize, usize, usize) {
    thread::sleep_ticks(3); // let exited threads' stacks be freed
    (thread::thread_count(), process::count(), memory::frames_in_use(), net::unix::bound_count())
}

/// Run one group twice (the first warms caches), check it passes and
/// nothing is left behind.
fn run_without_leaks(group: &str) {
    assert_eq!(run(&[group]), ExitStatus::Exited(0), "unixtest {} failed (warm-up)", group);
    let before = counts();
    assert_eq!(run(&[group]), ExitStatus::Exited(0), "unixtest {} failed", group);
    assert_eq!(counts(), before, "unixtest {} leaked something", group);
    assert_eq!(net::unix::bound_count(), 0, "unixtest {} left bound sockets behind", group);
}

/// A server on a path (ramfs and ext2) echoes for a forked client;
/// abstract names, autobind, ECONNRESET/EPIPE/ECONNREFUSED, SO_PEERCRED.
#[test_case]
fn stream_echo_across_fork() {
    run_without_leaks("stream");
}

/// socketpair: stream (both ways, fork, shutdown, MSG_PEEK), seqpacket,
/// dgram, SOCK_CLOEXEC/SOCK_NONBLOCK.
#[test_case]
fn socketpairs() {
    run_without_leaks("pair");
}

/// Datagrams between named sockets: sender names, MSG_TRUNC, connect,
/// EPERM/EPROTOTYPE/EMSGSIZE/ECONNREFUSED.
#[test_case]
fn datagrams() {
    run_without_leaks("dgram");
}

/// SCM_RIGHTS: a file and a pipe end read through the received fds;
/// MSG_CTRUNC, MSG_CMSG_CLOEXEC, fds in flight closed with the socket
/// (and no frames leaked: see run_without_leaks); SCM_CREDENTIALS.
#[test_case]
fn passing_fds() {
    run_without_leaks("rights");
}

/// Nonblocking EAGAIN, real buffer limits (and a blocked writer), poll,
/// epoll EPOLLRDHUP/EPOLLHUP, nonblocking accept, full backlog.
#[test_case]
fn nonblocking_and_poll() {
    run_without_leaks("nonblock");
}

/// /proc/self/fd: listing, readlink (paths, pipe:[N], socket:[N]),
/// opening entries; /proc/self/exe.
#[test_case]
fn proc_self() {
    run_without_leaks("proc");
}

/// `--trace` knows the new calls by name.
#[test_case]
fn trace_names() {
    assert_eq!(syscall::trace_name(46), "sendmsg");
    assert_eq!(syscall::trace_name(47), "recvmsg");
    assert_eq!(syscall::trace_name(53), "socketpair");
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    rustbelt::test_panic_handler(info)
}