diff --git a/Makefile b/Makefile index b8cf0324..34311466 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ setup: @cargo install mdbook build: - @make --no-print-directory -C regression/ramdisk + @make --no-print-directory -C regression @cargo kbuild tools: @@ -33,4 +33,4 @@ check: clean: @cargo clean @cd docs && mdbook clean - @make --no-print-directory -C regression/ramdisk clean + @make --no-print-directory -C regression clean diff --git a/README.md b/README.md index eba94c01..d5ff8db6 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,23 @@ Then we can use the tool to check access control policy. cargo component-check ``` +### Syscall Test + +If we have already built the project for normal use, please clean the project before running syscall test. +```bash +make clean +``` + +For running syscall tests, we can run following command to build test binaries and run the OS for testing purpose. +```bash +ENABLE_SYSCALL_TEST=1 make run +``` + +Then, we can run the following script inside the OS to run all syscall test cases. +```bash +/opt/syscall_test/run_syscall_test.sh +``` + ## Code organization The codebase of Jinux is organized as below. diff --git a/boot/limine/scripts/limine-build.sh b/boot/limine/scripts/limine-build.sh index 69188a64..0c3c1458 100755 --- a/boot/limine/scripts/limine-build.sh +++ b/boot/limine/scripts/limine-build.sh @@ -25,7 +25,7 @@ cp target/limine/limine-cd.bin target/iso_root cp target/limine/limine-cd-efi.bin target/iso_root # Copy ramdisk -cp regression/ramdisk/build/ramdisk.cpio target/iso_root +cp regression/build/ramdisk.cpio target/iso_root xorriso -as mkisofs \ -b limine-cd.bin \ diff --git a/regression/.gitignore b/regression/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/regression/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/regression/ramdisk/Makefile b/regression/Makefile similarity index 53% rename from regression/ramdisk/Makefile rename to regression/Makefile index 1c8f4653..d07c8ab1 100644 --- a/regression/ramdisk/Makefile +++ b/regression/Makefile @@ -1,47 +1,45 @@ -MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically. -APPS := ../apps -BUILD_DIR := ./build -INITRAMFS := ./initramfs +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) +BUILD_DIR := $(CUR_DIR)/build +INITRAMFS := $(BUILD_DIR)/initramfs RAMDISK := $(BUILD_DIR)/ramdisk.cpio SHELL := /bin/bash -ifneq (, $(wildcard $(APPS)/. )) - APPS_DIRS := $(shell find $(APPS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true) - APPS_FILES := $(shell find $(APPS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true) -endif - ifneq (, $(wildcard $(INITRAMFS)/. )) INITRAMFS_DIRS := $(shell find $(INITRAMFS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true) INITRAMFS_FILES := $(shell find $(INITRAMFS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true) endif +.PHONY: all clean -.PHONY: all clean prepare_libs copy_special_files +all: build -all: $(RAMDISK) - -$(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES) +$(INITRAMFS): @rm -rf $@ && mkdir -p $@ -# Copy Apps - @cp -a $(APPS)/* $@ - @cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete -# Mkdir folders + @# Mkdir necessary folders @mkdir -p $@/tmp - @mkdir -p $@/test + @mkdir -p $@/opt @mkdir -p $@/lib64 @mkdir -p $@/lib/x86_64-linux-gnu -# Copy libs + @# Copy necessary libs @cp -L /lib64/ld-linux-x86-64.so.2 $@/lib64 @cp -L /lib/x86_64-linux-gnu/libc.so.6 $@/lib/x86_64-linux-gnu @cp -L /lib/x86_64-linux-gnu/libstdc++.so.6 $@/lib/x86_64-linux-gnu @cp -L /lib/x86_64-linux-gnu/libm.so.6 $@/lib/x86_64-linux-gnu @cp -L /lib/x86_64-linux-gnu/libgcc_s.so.1 $@/lib/x86_64-linux-gnu @cp -L /lib/x86_64-linux-gnu/libpthread.so.0 $@/lib/x86_64-linux-gnu + @# Copy from apps + @make --no-print-directory -C apps +ifeq ($(ENABLE_SYSCALL_TEST), 1) + @# Copy syscall test suite + @make --no-print-directory -C syscall_test +endif $(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES) @echo "Generating the ramdisk image..." - @rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR) - @./mkinitramfs $(INITRAMFS) $@ + @(cd $(INITRAMFS); find . | cpio -o -H newc) > $@ + +build: $(RAMDISK) clean: - @rm -rf $(INITRAMFS) $(BUILD_DIR) + @rm -rf $(BUILD_DIR) diff --git a/regression/apps/Makefile b/regression/apps/Makefile new file mode 100644 index 00000000..9de37f7f --- /dev/null +++ b/regression/apps/Makefile @@ -0,0 +1,12 @@ +MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically. +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) +INITRAMFS ?= $(CUR_DIR)/../build/initramfs + +.PHONY: all + +all: build + +build: + @cp -a $(CUR_DIR)/* $(INITRAMFS) + @cd $(INITRAMFS) && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete diff --git a/regression/ramdisk/mkinitramfs b/regression/ramdisk/mkinitramfs deleted file mode 100755 index 52b8fc54..00000000 --- a/regression/ramdisk/mkinitramfs +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -e - -if [ $# -ne 2 ]; then - echo "Usage: mkinitramfs " - exit 1 -fi - -if [ -d "$1" ]; then - echo "Creating $2 from $1" - (cd "$1"; find . | cpio -o -H newc) > "$2" -else - echo "The first argument must be a directory" - exit 1 -fi diff --git a/regression/syscall_test/Makefile b/regression/syscall_test/Makefile new file mode 100644 index 00000000..4b13e9ca --- /dev/null +++ b/regression/syscall_test/Makefile @@ -0,0 +1,43 @@ +TESTS ?= open_test read_test + +MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) +BUILD_DIR := $(CUR_DIR)/../build +SRC_DIR := $(BUILD_DIR)/gvisor_src +BIN_DIR := $(BUILD_DIR)/syscall_test_bins +INITRAMFS ?= $(CUR_DIR)/../build/initramfs +TARGET_DIR := $(INITRAMFS)/opt/syscall_test +RUN_BASH := $(CUR_DIR)/run_syscall_test.sh +BLOCK_LIST := $(CUR_DIR)/blocklists + +.PHONY: all clean + +all: $(TESTS) + +$(SRC_DIR): + @if ! type bazel > /dev/null; then \ + echo "bazel is not installed, please run $(CUR_DIR)/install_bazel.sh with sudo permission to install it."; \ + exit 1 ; \ + fi + @rm -rf $@ && mkdir -p $@ + @cd $@ && git clone -b 20200921.0 https://github.com/jinzhao-dev/gvisor.git . + +$(BIN_DIR): $(SRC_DIR) + @rm -rf $@ && mkdir -p $@ + @cd $(SRC_DIR) && bazel build --test_tag_filters=native //test/syscalls/... + @cp $(SRC_DIR)/bazel-bin/test/syscalls/linux/*_test $@ + +$(TARGET_DIR): $(RUN_BASH) $(BLOCK_LIST) $(BIN_DIR) + @rm -rf $@ && mkdir -p $@ + @# Prepare tests dir for test binaries + @mkdir $@/tests + @# Copy blocklists + @cp -rf $(BLOCK_LIST) $@ + @# Copy bash script + @cp -f $(RUN_BASH) $@ + +$(TESTS): $(TARGET_DIR) + @cp -f $(BIN_DIR)/$@ $(TARGET_DIR)/tests + +clean: + @rm -rf $(BIN_DIR) $(TARGET_DIR) diff --git a/regression/syscall_test/blocklists/open_test b/regression/syscall_test/blocklists/open_test new file mode 100644 index 00000000..108aca4a --- /dev/null +++ b/regression/syscall_test/blocklists/open_test @@ -0,0 +1,15 @@ +OpenTest.AppendConcurrentWrite +OpenTest.CanTruncateReadOnly +OpenTest.CanTruncateWithStrangePermissions +OpenTest.CanTruncateReadOnlyNoWritePermission_NoRandomSave +OpenTest.CanTruncateWriteOnlyNoReadPermission_NoRandomSave +OpenTest.FileNotDirectory +OpenTest.MustCreateExisting +OpenTest.NameTooLong +OpenTest.Null +OpenTest.OTrunc +OpenTest.OTruncAndReadOnlyDir +OpenTest.OpenNoFollowStillFollowsLinksInPath +OpenTest.OpenNonDirectoryWithTrailingSlash +OpenTest.SymlinkRecurse +OpenTest.Truncate \ No newline at end of file diff --git a/regression/syscall_test/blocklists/read_test b/regression/syscall_test/blocklists/read_test new file mode 100644 index 00000000..02c450bb --- /dev/null +++ b/regression/syscall_test/blocklists/read_test @@ -0,0 +1,2 @@ +ReadTest.EofAfterRead +ReadTest.ZeroBuffer \ No newline at end of file diff --git a/regression/syscall_test/install_bazel.sh b/regression/syscall_test/install_bazel.sh new file mode 100755 index 00000000..b64516b4 --- /dev/null +++ b/regression/syscall_test/install_bazel.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +set -e + +if type bazel > /dev/null; then + echo "Bazel has been installed already" + exit 0 +fi + +apt update && apt install curl gnupg -y +curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg +mv bazel.gpg /etc/apt/trusted.gpg.d/ + +echo 'deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8' | tee /etc/apt/sources.list.d/bazel.list +apt update && apt install bazel=5.4.0 -y + +echo "Bazel is installed successfully" diff --git a/regression/syscall_test/run_syscall_test.sh b/regression/syscall_test/run_syscall_test.sh new file mode 100755 index 00000000..449fb3cf --- /dev/null +++ b/regression/syscall_test/run_syscall_test.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +SCRIPT_DIR=$(dirname "$0") +TEST_BIN_DIR=$SCRIPT_DIR/tests +BLOCKLIST_DIR=$SCRIPT_DIR/blocklists +FAIL_CASES=$SCRIPT_DIR/fail_cases +BLOCK="" +TESTS=0 +PASSED_TESTS=0 +RESULT=0 +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +get_blocklist_subtests(){ + if [ -f $BLOCKLIST_DIR/$1 ]; then + BLOCK=$(sed ':a;N;$!ba;s/\n/:/g' $BLOCKLIST_DIR/$1) + return 0 + else + BLOCK="" + return 1 + fi +} + +run_one_test(){ + echo -e "Run Test Case: $1" + ret=0 + if [ -f $TEST_BIN_DIR/$1 ]; then + get_blocklist_subtests $1 + $TEST_BIN_DIR/$1 --gtest_filter=-$BLOCK + ret=$? + else + echo -e "Warning: $1 test does not exit" + ret=1 + fi + echo "" + return $ret +} + +rm -f $FAIL_CASES && touch $FAIL_CASES + +for syscall_test in $(find $TEST_BIN_DIR/. -name \*_test) ; do + test_name=$(basename "$syscall_test") + run_one_test $test_name + if [ $? -eq 0 ] && PASSED_TESTS=$((PASSED_TESTS+1));then + TESTS=$((TESTS+1)) + else + echo -e "$test_name" >> $FAIL_CASES + TESTS=$((TESTS+1)) + fi +done + +echo -e "$GREEN$PASSED_TESTS$NC of $GREEN$TESTS$NC test cases are passed." +[ $PASSED_TESTS -ne $TESTS ] && RESULT=1 +if [ $TESTS != $PASSED_TESTS ]; then + echo -e "The $RED$(($TESTS-$PASSED_TESTS))$NC failed test cases in this run are as follows:" + cat $FAIL_CASES +fi + +exit $RESULT diff --git a/services/libs/jinux-std/src/fs/file_table.rs b/services/libs/jinux-std/src/fs/file_table.rs index 28f9fcbb..40d8ee79 100644 --- a/services/libs/jinux-std/src/fs/file_table.rs +++ b/services/libs/jinux-std/src/fs/file_table.rs @@ -108,6 +108,23 @@ impl FileTable { entry.map(|e| e.file) } + pub fn close_all(&mut self) -> Vec> { + let mut closed_files = Vec::new(); + let closed_fds: Vec = self + .table + .idxes_and_items() + .map(|(idx, _)| idx as FileDescripter) + .collect(); + for fd in closed_fds { + let entry = self.table.remove(fd as usize).unwrap(); + let events = FdEvents::Close(fd); + self.notify_fd_events(&events); + entry.notify_fd_events(&events); + closed_files.push(entry.file); + } + closed_files + } + pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc> { self.table .get(fd as usize) diff --git a/services/libs/jinux-std/src/fs/pipe.rs b/services/libs/jinux-std/src/fs/pipe.rs index d829aadb..a813e19c 100644 --- a/services/libs/jinux-std/src/fs/pipe.rs +++ b/services/libs/jinux-std/src/fs/pipe.rs @@ -2,7 +2,9 @@ use crate::events::Observer; use crate::prelude::*; use super::file_handle::FileLike; -use super::utils::{AccessMode, Consumer, IoEvents, Poller, Producer, StatusFlags}; +use super::utils::{ + AccessMode, Consumer, InodeMode, InodeType, IoEvents, Metadata, Poller, Producer, StatusFlags, +}; pub struct PipeReader { consumer: Consumer, @@ -55,6 +57,25 @@ impl FileLike for PipeReader { AccessMode::O_RDONLY } + fn metadata(&self) -> Metadata { + Metadata { + dev: 0, + ino: 0, + size: 0, + blk_size: 0, + blocks: 0, + atime: Default::default(), + mtime: Default::default(), + ctime: Default::default(), + type_: InodeType::NamedPipe, + mode: InodeMode::from_bits_truncate(0o400), + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + } + } + fn register_observer( &self, observer: Weak>, @@ -122,6 +143,25 @@ impl FileLike for PipeWriter { AccessMode::O_WRONLY } + fn metadata(&self) -> Metadata { + Metadata { + dev: 0, + ino: 0, + size: 0, + blk_size: 0, + blocks: 0, + atime: Default::default(), + mtime: Default::default(), + ctime: Default::default(), + type_: InodeType::NamedPipe, + mode: InodeMode::from_bits_truncate(0o200), + nlinks: 1, + uid: 0, + gid: 0, + rdev: 0, + } + } + fn register_observer( &self, observer: Weak>, diff --git a/services/libs/jinux-std/src/process/mod.rs b/services/libs/jinux-std/src/process/mod.rs index fd099fe2..b6e2f421 100644 --- a/services/libs/jinux-std/src/process/mod.rs +++ b/services/libs/jinux-std/src/process/mod.rs @@ -270,6 +270,11 @@ impl Process { for thread in &*self.threads.lock() { thread.exit(); } + // close all files then exit the process + let files = self.file_table().lock().close_all(); + for file in files { + let _ = file.clean_for_close(); + } // move children to the init process if !self.is_init_process() { if let Some(init_process) = get_init_process() { diff --git a/services/libs/jinux-std/src/process/posix_thread/name.rs b/services/libs/jinux-std/src/process/posix_thread/name.rs index 3231f165..cd689a81 100644 --- a/services/libs/jinux-std/src/process/posix_thread/name.rs +++ b/services/libs/jinux-std/src/process/posix_thread/name.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -pub const MAX_THREAD_NAME_LEN: usize = 256; +pub const MAX_THREAD_NAME_LEN: usize = 16; #[derive(Debug)] pub struct ThreadName { diff --git a/services/libs/jinux-std/src/syscall/constants.rs b/services/libs/jinux-std/src/syscall/constants.rs index e175eb88..0c9a4f91 100644 --- a/services/libs/jinux-std/src/syscall/constants.rs +++ b/services/libs/jinux-std/src/syscall/constants.rs @@ -4,5 +4,5 @@ pub const MAX_FILENAME_LEN: usize = 128; pub const MAX_ARGV_NUMBER: usize = 128; pub const MAX_ENVP_NUMBER: usize = 128; -pub const MAX_ARG_LEN: usize = 128; +pub const MAX_ARG_LEN: usize = 2048; pub const MAX_ENV_LEN: usize = 128; diff --git a/services/libs/jinux-std/src/syscall/pipe.rs b/services/libs/jinux-std/src/syscall/pipe.rs index 015f2a1b..973163d7 100644 --- a/services/libs/jinux-std/src/syscall/pipe.rs +++ b/services/libs/jinux-std/src/syscall/pipe.rs @@ -28,6 +28,7 @@ pub fn sys_pipe2(fds: Vaddr, flags: u32) -> Result { let mut file_table = current.file_table().lock(); pipe_fds.reader_fd = file_table.insert(pipe_reader); pipe_fds.writer_fd = file_table.insert(pipe_writer); + debug!("pipe_fds: {:?}", pipe_fds); write_val_to_user(fds, &pipe_fds)?; Ok(SyscallReturn::Return(0))