337 lines
11 KiB
Rust
337 lines
11 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use sctrace::{CliReporterBuilder, Patterns, SctraceBuilder, StraceLogStream};
|
|
|
|
#[test]
|
|
fn test_open_syscall() {
|
|
let scml_content = r#"
|
|
access_mode =
|
|
O_RDONLY |
|
|
O_WRONLY |
|
|
O_RDWR;
|
|
creation_flags =
|
|
O_CLOEXEC |
|
|
O_DIRECTORY |
|
|
O_EXCL |
|
|
O_NOCTTY |
|
|
O_NOFOLLOW |
|
|
O_TRUNC;
|
|
status_flags =
|
|
O_APPEND |
|
|
O_ASYNC |
|
|
O_DIRECT |
|
|
O_LARGEFILE |
|
|
O_NOATIME |
|
|
O_NONBLOCK |
|
|
O_SYNC;
|
|
|
|
// Open an existing file
|
|
open(
|
|
path,
|
|
flags = <access_mode> | <creation_flags> | <status_flags>,
|
|
);
|
|
openat(
|
|
dirfd,
|
|
path,
|
|
flags = <access_mode> | <creation_flags> | <status_flags>,
|
|
);
|
|
|
|
// Create a new file
|
|
open(
|
|
path,
|
|
flags = O_CREAT | <access_mode> | <creation_flags> | <status_flags>,
|
|
mode
|
|
);
|
|
openat(
|
|
dirfd,
|
|
path,
|
|
flags = O_CREAT | <access_mode> | <creation_flags> | <status_flags>,
|
|
mode
|
|
);
|
|
|
|
// Status flags that are meaningful with O_PATH
|
|
opath_valid_flags = O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW;
|
|
// All other flags are ignored with O_PATH
|
|
opath_ignored_flags = O_CREAT | <creation_flags> | <status_flags>;
|
|
// Obtain a file descriptor to indicate a location in FS
|
|
open(
|
|
path,
|
|
flags = O_PATH | <opath_valid_flags> | <opath_ignored_flags>
|
|
);
|
|
openat(
|
|
dirfd,
|
|
path,
|
|
flags = O_PATH | <opath_valid_flags> | <opath_ignored_flags>
|
|
);
|
|
|
|
// Create an unnamed file
|
|
// open(path, flags = O_TMPFILE | <creation_flags> | <status_flags>)
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
|
|
open("/dev/tdx_guest", O_RDWR|O_NONBLOCK) = 3
|
|
open("/tmp/sctrace_testfile", O_CREAT|O_RDWR|O_CLOEXEC, 0666) = 4
|
|
openat(AT_FDCWD, "/tmp/sctrace_testfile2", O_PATH|O_CREAT) = 5
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_timer_create_syscall() {
|
|
let scml_content = r#"
|
|
opt_notify_methods = SIGEV_NONE | SIGEV_SIGNAL | SIGEV_THREAD_ID;
|
|
|
|
// Create a timer with predefined clock source
|
|
timer_create(
|
|
clockid = CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID | CLOCK_REALTIME | CLOCK_MONOTONIC | CLOCK_BOOTTIME,
|
|
sevp = {
|
|
sigev_notify = <opt_notify_methods>,
|
|
..
|
|
},
|
|
timerid
|
|
);
|
|
|
|
// Create a timer based on a per-process or per-thread clock
|
|
timer_create(
|
|
clockid = <INTEGER>,
|
|
sevp = {
|
|
sigev_notify = <opt_notify_methods>,
|
|
..
|
|
},
|
|
timerid
|
|
);
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
timer_create(CLOCK_REALTIME, {sigev_notify=SIGEV_SIGNAL, sigev_signo=SIGALRM, sigev_value={sival_ptr=0x559b4d3e2e70}}, 0x7ffcb1f4d9c0) = 0
|
|
timer_create(0xff5be79e /* CLOCK_??? */, {sigev_value={sival_int=565425088, sival_ptr=0x562221b3b3c0}, sigev_signo=SIGRTMIN, sigev_notify=SIGEV_THREAD_ID, sigev_notify_thread_id=1344269}, [0]) = 0
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_struct_with_same_name() {
|
|
let scml_content = r#"
|
|
struct cmsghdr = {
|
|
cmsg_level = SOL_SOCKET,
|
|
cmsg_type = SO_TIMESTAMP_OLD | SCM_RIGHTS | SCM_CREDENTIALS,
|
|
..
|
|
};
|
|
struct cmsghdr = {
|
|
cmsg_level = SOL_IP,
|
|
cmsg_type = IP_TTL,
|
|
..
|
|
};
|
|
|
|
// Rule for message header, which refers to the rules for control message header
|
|
struct msghdr = {
|
|
msg_control = [ <cmsghdr> ],
|
|
..
|
|
};
|
|
|
|
recvmsg(socket, message = <msghdr>, flags);
|
|
"#;
|
|
|
|
let log_lines = &[
|
|
"recvmsg(4, {msg_name=NULL, msg_namelen=0, msg_iov=NULL, msg_iovlen=0, msg_control=[{cmsg_len=16, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS}], msg_controllen=16, msg_flags=0}, 0) = 24",
|
|
"recvmsg(5, {msg_name=NULL, msg_namelen=0, msg_iov=NULL, msg_iovlen=0, msg_control=[{cmsg_len=16, cmsg_level=SOL_IP, cmsg_type=IP_TTL}], msg_controllen=16, msg_flags=0}, 0) = 24",
|
|
"recvmsg(6, {msg_name=NULL, msg_namelen=0, msg_iov=NULL, msg_iovlen=0, msg_control=[{cmsg_len=16, cmsg_level=SOL_IPV6, cmsg_type=IPV6_UNICAST_HOPS}], msg_controllen=16, msg_flags=0}, 0) = 24",
|
|
];
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines.join("\n").as_str()).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 1);
|
|
assert_eq!(result[0], format!("Unsupported syscall: {}", log_lines[2]));
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_syscall() {
|
|
let scml_content = r#"
|
|
signal_flags = SIGHUP | SIGINT | SIGQUIT | SIGILL |
|
|
SIGTRAP | SIGABRT | SIGSTKFLT | SIGFPE |
|
|
SIGKILL | SIGBUS | SIGSEGV | SIGXCPU |
|
|
SIGPIPE | SIGALRM | SIGTERM | SIGUSR1 |
|
|
SIGUSR2 | SIGCHLD | SIGPWR | SIGVTALRM |
|
|
SIGPROF | SIGIO | SIGWINCH | SIGSTOP |
|
|
SIGTSTP | SIGCONT | SIGTTIN | SIGTTOU |
|
|
SIGURG | SIGXFSZ | SIGSYS | SIGRTMIN;
|
|
|
|
opt_flags =
|
|
// Optional flags
|
|
//
|
|
// Share the parent's virtual memory
|
|
CLONE_VM |
|
|
// Share the parent's filesystem
|
|
CLONE_FS |
|
|
// Share the parent's file descriptor table
|
|
CLONE_FILES |
|
|
// Share the parent's signal handlers
|
|
CLONE_SIGHAND |
|
|
// Place child in the same thread group as parent
|
|
CLONE_THREAD |
|
|
// Share the parent's System V semaphore adjustments
|
|
CLONE_SYSVSEM |
|
|
// Suspend parent until the child exits or calls `execve`
|
|
CLONE_VFORK |
|
|
// Create a new mount namespace for the child
|
|
CLONE_NEWNS |
|
|
// Write child `TID` to parent's memory
|
|
CLONE_PARENT_SETTID |
|
|
// Allocate a `PID` file descriptor for the child
|
|
CLONE_PIDFD |
|
|
// Set thread-local storage for the child
|
|
CLONE_SETTLS |
|
|
// Write child `TID` to child's memory
|
|
CLONE_CHILD_SETTID |
|
|
// Clear child `TID` in child's memory on exit
|
|
CLONE_CHILD_CLEARTID |
|
|
// Make the child's parent the same as the caller's parent
|
|
CLONE_PARENT;
|
|
|
|
// Create a thread or process
|
|
clone(
|
|
fn, stack,
|
|
flags = <opt_flags> | <signal_flags>,
|
|
func_arg, ..
|
|
);
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7745c1ca10) = 141614
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiple_threads_syscalls() {
|
|
let scml_content = r#"
|
|
wait4(
|
|
pid, wstatus,
|
|
options = WNOHANG | WSTOPPED | WCONTINUED | WNOWAIT,
|
|
rusage
|
|
);
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
141611 wait4(-1, <unfinished ...>
|
|
141611 <... wait4 resumed>[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG, NULL) = 141612
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_check_logfile_wildcard_pattern() {
|
|
let scml_content = r#"
|
|
openat(dirfd, pathname, flags, ..);
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
|
|
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC, 0755) = 4
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_check_program_simple_command() {
|
|
let scml_content = r#"
|
|
execve(filename, argv, envp);
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::run_cmd("/bin/true", vec![]).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert!(result.iter().all(|error| !error.contains("execve(")));
|
|
}
|
|
|
|
#[test]
|
|
fn test_heterogeneous_arrays() {
|
|
let scml_content = r#"
|
|
struct iovec = {
|
|
iov_base = [
|
|
[
|
|
{
|
|
nlmsg_type = RTM_NEWADDR,
|
|
..
|
|
},
|
|
[
|
|
[ { nla_type = IFA_CACHEINFO, .. } ]
|
|
]
|
|
]
|
|
],
|
|
..
|
|
};
|
|
recvmsg(
|
|
sockfd,
|
|
msg = {
|
|
msg_iov = [ <iovec> ],
|
|
..
|
|
},
|
|
flags
|
|
);
|
|
"#;
|
|
|
|
let log_lines = r#"
|
|
1370921 recvmsg(7<socket:[181857552]>, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=0, msg_iov=[{iov_base=[[{nlmsg_len=76, nlmsg_type=RTM_NEWADDR, nlmsg_flags=NLM_F_MULTI, nlmsg_seq=1758615457, nlmsg_pid=1370920}, [[{nla_len=20, nla_type=IFA_CACHEINFO}]]]], iov_len=4096}], msg_iovlen=1, msg_control=NULL, msg_controllen=0, msg_flags=0}, 0) = 1280
|
|
"#;
|
|
|
|
let sctrace = SctraceBuilder::new()
|
|
.patterns(Patterns::from_scml(scml_content).unwrap())
|
|
.strace(StraceLogStream::from_string(log_lines).unwrap())
|
|
.reporter(CliReporterBuilder::new().quiet().collect().build())
|
|
.build();
|
|
|
|
let result = sctrace.run().unwrap().unwrap();
|
|
assert_eq!(result.len(), 0);
|
|
}
|