Skip to content

Commit 96da0cf

Browse files
committed
restructure files, added README
1 parent 828f25f commit 96da0cf

File tree

6 files changed

+410
-0
lines changed

6 files changed

+410
-0
lines changed

examples/strace-grate/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
:Q
2+
## strace-grate
3+
4+
strace-grate is a lightweight utility for tracing system calls. It outputs detailed system call traces along with passed arguments (print paths, associated file descriptors, etc) and their return values.
5+
6+
### strace APIs
7+
8+
strace-grate provides a macro named `DEFINE_HANDLER` that allows user to specify syscalls to be traced (all syscalls are traced by default). It facilitates syscall interposing, logging and forwarding.
9+
10+
Defination:
11+
12+
```
13+
Arguments:
14+
// 1st: syscall name
15+
// 2nd: syscall number
16+
// 3rd - 8th: ARG type (ARG_INT || ARG_PTR || ARG_STR)
17+
//
18+
// NOTE: if unsure of ARG_TYPE follow:
19+
// https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/
20+
21+
DEFINE_HANDLER(syscall_name, syscall_number, ARG_TYPE_1, ..., ARG_TYPE_6)
22+
```
23+
24+
Example:
25+
```
26+
DEFINE_HANDLER(read, 0, ARG_INT, ARG_PTR, ARG_INT)
27+
```
28+
29+
## Building
30+
31+
Uses [lind_compile](https://github.com/Lind-Project/lind-wasm/blob/main/scripts/lind_compile) script with `--compile-grate` flag.
32+
33+
`lind_compile --compile-grate src/strace_grate.c src/strace.c`
34+
35+
or use the `compile_grate.sh` script to build strace grate.
36+
37+
## Implementation
38+
39+
### Interposing
40+
41+
Lind's `threei` subsystem enables syscall interposition. Each syscall is implemented using the `DEFINE_HANDLER` macro, which generates a dedicated handler function for that syscall.
42+
43+
Every handler maintains a log_buffer used to record relevant execution details. When syscalls pass arguments of type string (e.g., pointers to paths or filenames), the handler invokes the `copy_data_between_cages()` helper from threei to safely dereference the value. This allows the system to resolve and log meaningful data such as file paths.
44+
45+
After preprocessing arguments, the handler forwards the syscall to the target cage (i.e., the application running under strace-grate) using `make_threei_call()`. Once the syscall completes, the handler logs the return value and writes the contents of `log_buffer` to `stderr`.
46+
47+
### Syscall Handler Table
48+
49+
All syscall handlers are stored in a centralized syscall handler table `syscall_handler_table`. The `DEFINE_HANDLER` macro not only generates the handler function `<name>_grate` but also defines a corresponding `register_<name>()` function.
50+
51+
This registration function ensures it is executed automatically at grate startup. During this initialization phase, each constructor inserts its associated handler into the table by assigning the function pointer to the appropriate syscall index
52+
53+
## Testing
54+
55+
To validate the implementation, a script is provided that runs Lind's unit test suite under strace-grate. Because strace-grate intercepts and logs all syscalls supported by Lind, this approach ensures comprehensive testing of the interposition layer and verifies that syscall handling behaves correctly across a wide range of scenarios.
56+
57+
## Example Usage:
58+
59+
`lind_run strace_grate.cwasm app.cwasm`
60+
61+
## Future Work
62+
63+
- Flags for tracing user specified system calls.
64+
- Hex to ASCII dump of arguments (e.g flags, modes, etc).
65+
- Add syscall counter and error logging.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
cd "$(dirname "$0")"
6+
lind_compile --compile-grate src/strace_grate.c src/strace.c

examples/strace-grate/src/strace.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include <stdio.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
#include "strace.h"
5+
6+
// table for storing syscall handlers
7+
syscall_handler_t syscall_handler_table[MAX_SYSCALLS] = {0};
8+
9+
// defined syscall handlers
10+
//
11+
// args:
12+
// 1st: syscall name
13+
// 2nd: syscall number
14+
// 3rd - 8th: ARG type (ARG_INT || ARG_PTR || ARG_STR)
15+
//
16+
// NOTE: if unsure of ARG_TYPE follow:
17+
// https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/
18+
//
19+
// defines handler for all syscalls supported by lind
20+
21+
DEFINE_HANDLER(read, 0, ARG_INT, ARG_PTR, ARG_INT)
22+
DEFINE_HANDLER(write, 1, ARG_INT, ARG_PTR, ARG_INT)
23+
DEFINE_HANDLER(open, 2, ARG_STR, ARG_INT, ARG_INT)
24+
DEFINE_HANDLER(close, 3, ARG_INT)
25+
DEFINE_HANDLER(stat, 4, ARG_STR, ARG_PTR)
26+
DEFINE_HANDLER(fstat, 5, ARG_INT, ARG_PTR)
27+
DEFINE_HANDLER(poll, 7, ARG_PTR, ARG_INT, ARG_INT)
28+
DEFINE_HANDLER(lseek, 8, ARG_INT, ARG_INT, ARG_INT)
29+
DEFINE_HANDLER(mmap, 9, ARG_PTR, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT)
30+
DEFINE_HANDLER(mprotect, 10, ARG_PTR, ARG_INT, ARG_INT)
31+
DEFINE_HANDLER(munmap, 11, ARG_PTR, ARG_INT)
32+
DEFINE_HANDLER(brk, 12, ARG_PTR)
33+
DEFINE_HANDLER(sigaction, 13, ARG_INT, ARG_PTR, ARG_PTR)
34+
DEFINE_HANDLER(sigprocmask, 14, ARG_INT, ARG_PTR, ARG_PTR)
35+
DEFINE_HANDLER(ioctl, 16, ARG_INT, ARG_INT, ARG_PTR)
36+
DEFINE_HANDLER(pread, 17, ARG_INT, ARG_PTR, ARG_INT, ARG_INT)
37+
DEFINE_HANDLER(pwrite, 18, ARG_INT, ARG_PTR, ARG_INT, ARG_INT)
38+
DEFINE_HANDLER(writev, 20, ARG_INT, ARG_PTR, ARG_INT)
39+
DEFINE_HANDLER(access, 21, ARG_STR, ARG_INT)
40+
DEFINE_HANDLER(pipe, 22, ARG_PTR)
41+
DEFINE_HANDLER(select, 23, ARG_INT, ARG_PTR, ARG_PTR, ARG_PTR, ARG_PTR)
42+
DEFINE_HANDLER(sched_yield, 24)
43+
DEFINE_HANDLER(shmget, 29, ARG_INT, ARG_INT, ARG_INT)
44+
DEFINE_HANDLER(shmat, 30, ARG_INT, ARG_PTR, ARG_INT)
45+
DEFINE_HANDLER(shmctl, 31, ARG_INT, ARG_INT, ARG_PTR)
46+
DEFINE_HANDLER(dup, 32, ARG_INT)
47+
DEFINE_HANDLER(dup2, 33, ARG_INT, ARG_INT)
48+
DEFINE_HANDLER(nanosleep, 35, ARG_PTR, ARG_PTR)
49+
DEFINE_HANDLER(setitimer, 38, ARG_INT, ARG_PTR, ARG_PTR)
50+
DEFINE_HANDLER(getpid, 39)
51+
DEFINE_HANDLER(socket, 41, ARG_INT, ARG_INT, ARG_INT)
52+
DEFINE_HANDLER(connect, 42, ARG_INT, ARG_PTR, ARG_INT)
53+
DEFINE_HANDLER(accept, 43, ARG_INT, ARG_PTR, ARG_PTR)
54+
DEFINE_HANDLER(sendto, 44, ARG_INT, ARG_PTR, ARG_INT, ARG_INT, ARG_PTR, ARG_INT)
55+
DEFINE_HANDLER(recvfrom, 45, ARG_INT, ARG_PTR, ARG_INT, ARG_INT, ARG_PTR, ARG_PTR)
56+
DEFINE_HANDLER(shutdown, 48, ARG_INT, ARG_INT)
57+
DEFINE_HANDLER(bind, 49, ARG_INT, ARG_PTR, ARG_INT)
58+
DEFINE_HANDLER(listen, 50, ARG_INT, ARG_INT)
59+
DEFINE_HANDLER(getsockname, 51, ARG_INT, ARG_PTR, ARG_PTR)
60+
DEFINE_HANDLER(getpeername, 52, ARG_INT, ARG_PTR, ARG_PTR)
61+
DEFINE_HANDLER(socketpair, 53, ARG_INT, ARG_INT, ARG_INT, ARG_PTR)
62+
DEFINE_HANDLER(setsockopt, 54, ARG_INT, ARG_INT, ARG_INT, ARG_PTR, ARG_INT)
63+
DEFINE_HANDLER(getsockopt, 55, ARG_INT, ARG_INT, ARG_INT, ARG_PTR, ARG_PTR)
64+
DEFINE_HANDLER(clone, 56, ARG_INT, ARG_PTR, ARG_PTR, ARG_PTR, ARG_PTR)
65+
DEFINE_HANDLER(fork, 57)
66+
DEFINE_HANDLER(exec, 59, ARG_STR, ARG_PTR, ARG_PTR)
67+
DEFINE_HANDLER(exit, 60, ARG_INT)
68+
DEFINE_HANDLER(waitpid, 61, ARG_INT, ARG_PTR, ARG_INT)
69+
DEFINE_HANDLER(kill, 62, ARG_INT, ARG_INT)
70+
DEFINE_HANDLER(shmdt, 67, ARG_PTR)
71+
DEFINE_HANDLER(fcntl, 72, ARG_INT, ARG_INT, ARG_PTR)
72+
DEFINE_HANDLER(flock, 73, ARG_INT, ARG_INT)
73+
DEFINE_HANDLER(fsync, 74, ARG_INT)
74+
DEFINE_HANDLER(fdatasync, 75, ARG_INT)
75+
DEFINE_HANDLER(truncate, 76, ARG_STR, ARG_INT)
76+
DEFINE_HANDLER(ftruncate, 77, ARG_INT, ARG_INT)
77+
DEFINE_HANDLER(getdents, 78, ARG_INT, ARG_PTR, ARG_INT)
78+
DEFINE_HANDLER(getcwd, 79, ARG_PTR, ARG_INT)
79+
DEFINE_HANDLER(chdir, 80, ARG_STR)
80+
DEFINE_HANDLER(fchdir, 81, ARG_INT)
81+
DEFINE_HANDLER(rename, 82, ARG_STR, ARG_STR)
82+
DEFINE_HANDLER(mkdir, 83, ARG_STR, ARG_INT)
83+
DEFINE_HANDLER(rmdir, 84, ARG_STR)
84+
DEFINE_HANDLER(link, 86, ARG_STR, ARG_STR)
85+
DEFINE_HANDLER(unlink, 87, ARG_STR)
86+
DEFINE_HANDLER(readlink, 89, ARG_STR, ARG_PTR, ARG_INT)
87+
DEFINE_HANDLER(chmod, 90, ARG_STR, ARG_INT)
88+
DEFINE_HANDLER(fchmod, 91, ARG_INT, ARG_INT)
89+
DEFINE_HANDLER(getuid, 102)
90+
DEFINE_HANDLER(getgid, 104)
91+
DEFINE_HANDLER(geteuid, 107)
92+
DEFINE_HANDLER(getegid, 108)
93+
DEFINE_HANDLER(getppid, 110)
94+
DEFINE_HANDLER(statfs, 137, ARG_STR, ARG_PTR)
95+
DEFINE_HANDLER(fstatfs, 138, ARG_INT, ARG_PTR)
96+
DEFINE_HANDLER(gethostname, 170, ARG_PTR, ARG_INT)
97+
DEFINE_HANDLER(futex, 202, ARG_PTR, ARG_INT, ARG_INT, ARG_PTR, ARG_PTR, ARG_INT)
98+
DEFINE_HANDLER(epoll_create, 213, ARG_INT)
99+
DEFINE_HANDLER(clock_gettime, 228, ARG_INT, ARG_PTR)
100+
DEFINE_HANDLER(epoll_wait, 232, ARG_INT, ARG_PTR, ARG_INT, ARG_INT)
101+
DEFINE_HANDLER(epoll_ctl, 233, ARG_INT, ARG_INT, ARG_INT, ARG_PTR)
102+
DEFINE_HANDLER(unlinkat, 263, ARG_INT, ARG_STR, ARG_INT)
103+
DEFINE_HANDLER(readlinkat, 267, ARG_INT, ARG_STR, ARG_PTR, ARG_INT)
104+
DEFINE_HANDLER(sync_file_range, 277, ARG_INT, ARG_INT, ARG_INT, ARG_INT)
105+
DEFINE_HANDLER(epoll_create1, 291, ARG_INT)
106+
DEFINE_HANDLER(dup3, 292, ARG_INT, ARG_INT, ARG_INT)
107+
DEFINE_HANDLER(pipe2, 293, ARG_PTR, ARG_INT)
108+
DEFINE_HANDLER(getrandom, 318, ARG_PTR, ARG_INT, ARG_INT)
109+
110+
// dispatcher function
111+
int pass_fptr_to_wt(uint64_t fn_ptr_uint, uint64_t cageid,
112+
uint64_t arg1, uint64_t arg1cage,
113+
uint64_t arg2, uint64_t arg2cage,
114+
uint64_t arg3, uint64_t arg3cage,
115+
uint64_t arg4, uint64_t arg4cage,
116+
uint64_t arg5, uint64_t arg5cage,
117+
uint64_t arg6, uint64_t arg6cage) {
118+
119+
if (fn_ptr_uint == 0) {
120+
return -1;
121+
}
122+
123+
syscall_handler_t fn = (syscall_handler_t)(uintptr_t)fn_ptr_uint;
124+
125+
return fn(cageid, arg1, arg1cage, arg2, arg2cage, arg3, arg3cage,
126+
arg4, arg4cage, arg5, arg5cage, arg6, arg6cage);
127+
}

examples/strace-grate/src/strace.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#ifndef STRACE_H
2+
#define STRACE_H
3+
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <unistd.h>
8+
#include <lind_syscall.h>
9+
10+
#define ARG_INT 0
11+
#define ARG_STR 1
12+
#define ARG_PTR 2
13+
#define MAX_SYSCALLS 334
14+
15+
// function ptr for storing syscall handlers
16+
typedef int (*syscall_handler_t)(uint64_t, uint64_t, uint64_t, uint64_t,
17+
uint64_t, uint64_t, uint64_t, uint64_t,
18+
uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
19+
20+
// table for storing syscall handlers
21+
extern syscall_handler_t syscall_handler_table[MAX_SYSCALLS];
22+
23+
// macro for defining syscall handlers dynamically
24+
#define DEFINE_HANDLER(name, num, ...) \
25+
/* function defination for syscall handler */ \
26+
int name##_grate(uint64_t cageid, uint64_t arg1, uint64_t arg1cage, \
27+
uint64_t arg2, uint64_t arg2cage, \
28+
uint64_t arg3, uint64_t arg3cage, \
29+
uint64_t arg4, uint64_t arg4cage, \
30+
uint64_t arg5, uint64_t arg5cage, \
31+
uint64_t arg6, uint64_t arg6cage) { \
32+
int thiscage = getpid(); \
33+
int types[] = {__VA_ARGS__}; \
34+
int argsnum = sizeof(types) / sizeof(int); \
35+
uint64_t args[] = {arg1, arg2, arg3, arg4, arg5, arg6}; \
36+
uint64_t argcages[] = {arg1cage, arg2cage, arg3cage, arg4cage, \
37+
arg5cage, arg6cage}; \
38+
\
39+
char log_buffer[2048]; \
40+
int offset = 0; \
41+
\
42+
/* log buffer to print syscall with args and ret val */ \
43+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, "%s(", #name); \
44+
\
45+
for (int i = 0; i < argsnum; i++) { \
46+
if (i > 0) \
47+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, ", "); \
48+
\
49+
if (types[i] == ARG_STR && args[i] != 0) { \
50+
char *buf = malloc(256); \
51+
if (buf) { \
52+
copy_data_between_cages(thiscage, argcages[i], \
53+
args[i], argcages[i], \
54+
(uint64_t)buf, thiscage, \
55+
256, 1); \
56+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, \
57+
"\"%s\"", buf); \
58+
free(buf); \
59+
} else { \
60+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, \
61+
"0x%lx", (unsigned long)args[i]); \
62+
} \
63+
} else if (types[i] == ARG_PTR) { \
64+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, \
65+
"0x%lx", (unsigned long)args[i]); \
66+
} else { \
67+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, \
68+
"%ld", (long)args[i]); \
69+
} \
70+
} \
71+
\
72+
offset += snprintf(log_buffer + offset, sizeof(log_buffer) - offset, ")"); \
73+
\
74+
/* forward interposed syscall */ \
75+
int ret = make_threei_call(num, 0, \
76+
thiscage, arg1cage, \
77+
arg1, arg1cage, arg2, arg2cage, \
78+
arg3, arg3cage, arg4, arg4cage, \
79+
arg5, arg5cage, arg6, arg6cage, 0); \
80+
\
81+
fprintf(stderr, "%s = %d\n", log_buffer, ret); \
82+
return ret; \
83+
} \
84+
\
85+
/* constructor to store handler address in the table */ \
86+
__attribute__((constructor)) static void register_##name() { \
87+
syscall_handler_table[num] = &name##_grate; \
88+
}
89+
90+
#endif
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <sys/wait.h>
4+
#include <unistd.h>
5+
#include <stdlib.h>
6+
#include <semaphore.h>
7+
#include <sys/mman.h>
8+
#include "strace.h"
9+
10+
int main(int argc, char *argv[]) {
11+
if (argc < 2) {
12+
fprintf(stderr, "Usage: %s <cage_binary> [args...]\n", argv[0]);
13+
exit(EXIT_FAILURE);
14+
}
15+
16+
// using semaphores for synchronizing the grate and cage.
17+
//
18+
// this ensures that all the initalization is done by the grate.
19+
sem_t *sem = mmap(NULL, sizeof(*sem), PROT_READ | PROT_WRITE,
20+
MAP_SHARED | MAP_ANON, -1, 0);
21+
22+
int grateid = getpid();
23+
pid_t cageid = fork();
24+
25+
if (cageid < 0) {
26+
perror("fork failed");
27+
exit(EXIT_FAILURE);
28+
} else if (cageid == 0) {
29+
// wait for grate to register handlers
30+
sem_wait(sem);
31+
32+
if (execv(argv[1], &argv[1]) == -1) {
33+
perror("execv failed");
34+
exit(EXIT_FAILURE);
35+
}
36+
}
37+
38+
// loop to register syscall handlers
39+
for (int i = 0; i < MAX_SYSCALLS; i++) {
40+
if (syscall_handler_table[i] != NULL) {
41+
uint64_t fn_ptr = (uint64_t)(uintptr_t)syscall_handler_table[i];
42+
register_handler(cageid, i, grateid, fn_ptr);
43+
}
44+
}
45+
46+
// resume execution of the cage
47+
sem_post(sem);
48+
49+
int status;
50+
int w;
51+
52+
while (1) {
53+
w = wait(&status);
54+
if (w > 0) {
55+
break;
56+
}
57+
}
58+
59+
sem_destroy(sem);
60+
munmap(sem, sizeof(*sem));
61+
62+
return 0;
63+
}

0 commit comments

Comments
 (0)