Skip to content

Commit 81ffefd

Browse files
Add compile tools + new API support
1 parent 40547bc commit 81ffefd

File tree

7 files changed

+2735
-0
lines changed

7 files changed

+2735
-0
lines changed

tools/README.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
This folder contains example grate implementations, as well as an example runner for grate-cage configurations.
2+
3+
### Compile
4+
5+
`./tools/compile.sh <grate source files>`
6+
7+
### Run
8+
9+
- Compile all cages present in the `cage/` folder, and place them into `LIND_ROOT` using the `scripts/lind_compile.sh` command.
10+
- Compile all grates present in the `grates/` folder, and place them into `LIND_ROOT`.
11+
- Run an example grate configuration using `./tools/run.sh`. This will run the following configuration:
12+
13+
```
14+
geteuid_grate:
15+
getegid_grate.wasm:
16+
bash -c "etest & imfs_grate runopen & getgid_grate gidtest"
17+
```
18+
19+
### File Structure
20+
21+
```
22+
grates/
23+
imfs_grate.c // Grate source code with wrappers for individual syscalls.
24+
imfs.* // IMFS source code.
25+
... // Other Grate examples
26+
27+
cages/
28+
mash.c // Barebones, minimal bash source code.
29+
etest.c
30+
runopen.c
31+
... // Example cage source code.
32+
33+
syscalls // List of syscalls along with the arguments required.
34+
35+
tools/
36+
- wand.py // Python script that generates bindings for syscall conversions.
37+
- magic.*tmpl // Template files for .h and .c files with the necessary bindings.
38+
- compile.sh // Compilation script.
39+
- run.sh // Run runopen.c
40+
```
41+
42+
### Writing a syscall wrapper
43+
44+
Consider the example of the `xstat_grate` syscall wrapper written with the new API:
45+
46+
```c
47+
int xstat_syscall(int cageid, char *pathname, struct stat *statbuf) {
48+
return imfs_stat(cageid, pathname, statbuf);
49+
}
50+
```
51+
52+
The syscall wrapper only has to deal with parameters that a regular syscall declaration would take, along with the `cageid` parameter which points to the id of the cage that called this syscall.
53+
54+
Handling of copying data in or out of cages is handled internally. In this example, `pathname` already points to a valid memory address in this grate, and anything written to `*statbuf` is copied out to the appropriate location in the calling cage's memory.
55+
56+
For an example of a complete grate file, view `imfs_grate.c`. A short example is below:
57+
58+
```c
59+
#include <sys/types.h>
60+
#include <sys/stat.h>
61+
62+
#include <lind_syscall_num.h>
63+
#include "magic.h" // These are the headers required to use the bindings.
64+
65+
#include "imfs.h"
66+
67+
// grate_syscalls* are extern'd variables. These should be a list of syscall nums.
68+
int grate_syscalls[] = {XSTAT_SYSCALL};
69+
int grate_syscalls_len = 1;
70+
71+
// Optional initialization and destroy logic or the grate.
72+
void grate_init() {
73+
imfs_init();
74+
}
75+
76+
void grate_destroy() {
77+
printf("IMFS Exiting\n");
78+
}
79+
80+
int xstat_syscall(int cageid, char *pathname, struct stat *statbuf) {
81+
return imfs_stat(cageid, pathname, statbuf);
82+
}
83+
```
84+
85+
### Internals
86+
87+
The full implementation of how these bindings are generated can be seen in `tools/wand.py`, and `tools/compile.sh`.
88+
89+
We begin with a list of syscall declarations, which give us extra information about the parameters of a given syscall. Some examples are below:
90+
91+
```js
92+
xstat = {
93+
IN char* pathname
94+
OUT struct stat* statbuf
95+
}
96+
97+
read = {
98+
N int fd
99+
OUT void* buf[count]
100+
N size_t count
101+
}
102+
103+
write = {
104+
N int fd
105+
IN void* buf[count]
106+
N size_t count
107+
}
108+
```
109+
110+
The `IN/OUT/N` tags tell us when or if to call `copy_data_between_cages` for a particular argument. This is extrapolated using the type of the argument as listed on the man pages. For e.g. `const` pointers are not copied out. Reguar pointers are copied in before the syscall and copied out after it (useful for calls such as `recvmsg()`). Integer and integer-aliased types are not copied.
111+
112+
These tagged structs allow us to generate the `<syscall>_grate` functions that eventually call the user-defined wrappers. Some examples are cited below, for an example generated binding, view `magic.c` and `magic.h`.
113+
114+
115+
```c
116+
// Close doesn't require any copying of data.
117+
int close_grate(uint64_t cageid, ..., uint64_t arg6cage) {
118+
if (!close_syscall) {
119+
return -1;
120+
}
121+
122+
int fd = arg1;
123+
124+
int ret = close_syscall(cageid, fd);
125+
126+
return ret;
127+
}
128+
```
129+
130+
```c
131+
// XSTAT requires us to copy over the "pathname" before we call our wrapper, and call copy on "statbuf" to return the result to the calling cage.
132+
int xstat_grate(uint64_t cageid, ..., uint64_t arg6cage) {
133+
if (!xstat_syscall) {
134+
return -1;
135+
}
136+
137+
struct stat *statbuf = malloc(sizeof(struct stat));
138+
139+
if (statbuf == NULL) {
140+
perror("malloc failed");
141+
exit(EXIT_FAILURE);
142+
}
143+
144+
copy_data_between_cages(thiscage, arg2cage, arg2, arg2cage, (uint64_t)statbuf,
145+
thiscage, sizeof(struct stat), 0);
146+
147+
char *pathname = malloc(256);
148+
149+
if (pathname == NULL) {
150+
perror("malloc failed");
151+
exit(EXIT_FAILURE);
152+
}
153+
154+
copy_data_between_cages(thiscage, arg1cage, arg1, arg1cage,
155+
(uint64_t)pathname, thiscage, 256, 1);
156+
157+
int ret = xstat_syscall(cageid, pathname, statbuf);
158+
159+
if (arg2 != 0) {
160+
copy_data_between_cages(thiscage, arg2cage, (uint64_t)statbuf, thiscage,
161+
arg2, arg2cage, sizeof(struct stat), 0);
162+
}
163+
164+
free(statbuf);
165+
free(pathname);
166+
167+
return ret;
168+
}
169+
```
170+
171+
#### Notes on dealing with a `buffer` vs `char *`:
172+
173+
When allocating memory for "strings", there are two distinct cases to handle.
174+
175+
For a `char *`, for now we assign a constant `256` bytes of memory and copies are done using `StrCpy` mode (relying on `\0` as the terminating character). Example - `char * pathname` in `open`)
176+
177+
For buffers, (`read/write` has `void buf[count]`), we allocate the mentioned size of memory, and use `RawCpy` to copy exactly this size.
178+
179+
For non-string pointers (such as `struct stat *statbuf`), we allocate `sizeof(<type>)` bytes, and use `RawCpy`.
180+
181+
The current bindings generator implicitly handles all these cases.

tools/compile.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/bin/bash
2+
3+
4+
mkdir -p output
5+
6+
cat ./tools/magic.tmpl > magic.h
7+
python3 ./tools/wand.py 0 >> magic.h
8+
9+
cat ./tools/magic.ctmpl > magic.c
10+
python3 ./tools/wand.py 1 >> magic.c
11+
12+
# IF=("$@")
13+
# F="${IF[0]}"
14+
# B="$(basename "$F" .c)"
15+
# OUT="${B}"
16+
17+
# /home/lind/lind-wasm/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04/bin/clang -pthread --target=wasm32-unknown-wasi --sysroot /home/lind/lind-wasm/src/glibc/sysroot -Wl,--import-memory,--export-memory,--max-memory=1570242560,--export=signal_callback,--export=__stack_pointer,--export=__stack_low,--export=open_grate,--export=close_grate,--export=geteuid_grate,--export=getegid_grate,--export-table magic.c "$@" -g -DLIB -DDIAG -D_GNU_SOURCE -O0 -o output/${OUT}.wasm && /home/lind/lind-wasm/tools/binaryen/bin/wasm-opt --epoch-injection --asyncify -O2 --debuginfo output/${OUT}.wasm -o output/${OUT}.wasm && /home/lind/lind-wasm/src/wasmtime/target/release/wasmtime compile output/${OUT}.wasm -o output/${OUT}.cwasm
18+
19+
# rm magic.c magic.h
20+
21+
if [ $# -lt 1 ]; then
22+
echo "Usage: $0 <source.c>"
23+
exit 1
24+
fi
25+
26+
SRC="$1"
27+
BASE="${SRC%.c}"
28+
29+
CLANG="/home/lind/lind-wasm/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04/bin/clang"
30+
SYSROOT="/home/lind/lind-wasm/src/glibc/sysroot"
31+
WASM_OPT="/home/lind/lind-wasm/tools/binaryen/bin/wasm-opt"
32+
WASMTIME="/home/lind/lind-wasm/src/wasmtime/target/release/wasmtime"
33+
34+
"$CLANG" -pthread \
35+
--target=wasm32-unknown-wasi \
36+
--sysroot "$SYSROOT" \
37+
-Wl,--import-memory,--export-memory,--max-memory=67108864,\
38+
--export=__stack_pointer,--export=__stack_low,--export=pass_fptr_to_wt \
39+
"$SRC" \
40+
-g -O0 -o "${BASE}.wasm"
41+
42+
"$WASM_OPT" --asyncify --epoch-injection --debuginfo "${BASE}.wasm" -o "${BASE}.wasm"
43+
44+
"$WASMTIME" compile "${BASE}.wasm" -o "${BASE}.cwasm"

tools/magic.ctmpl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
This is an auto-generated file. Do not modify.
3+
*/
4+
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <unistd.h>
8+
#include <register_handler.h>
9+
#include <sys/types.h>
10+
#include <sys/wait.h>
11+
12+
#include <sys/stat.h>
13+
#include <sys/utsname.h>
14+
15+
#include <lind_syscall_num.h>
16+
#include "magic.h"
17+
18+
extern int grate_syscalls[];
19+
extern size_t grate_syscalls_len;
20+
21+
void register_handlers(int cageid, int grateid) {
22+
int ret;
23+
for(int i=0; i < grate_syscalls_len; i++) {
24+
ret = register_handler(cageid, grate_syscalls[i], grateid, grateid);
25+
printf("Registered: %d\n", grate_syscalls[i]);
26+
}
27+
}
28+
29+
int main(int argc, char *argv[]) {
30+
if (argc < 2) {
31+
fprintf(stderr, "Usage: %s <cage_file>\n", argv[0]);
32+
exit(EXIT_FAILURE);
33+
}
34+
35+
int grateid = getpid();
36+
printf("grateid: %d\n", grateid);
37+
38+
pid_t pid = fork();
39+
if (pid < 0) {
40+
perror("fork failed");
41+
exit(EXIT_FAILURE);
42+
} else if (pid == 0) {
43+
int cageid = getpid();
44+
printf("cageid: %d\n", cageid);
45+
register_handlers(cageid, grateid);
46+
47+
sleep(3);
48+
printf("Execing %s\n", argv[1]);
49+
if (execv(argv[1], &argv[1]) == -1) {
50+
printf("Execv failed\n");
51+
exit(EXIT_FAILURE);
52+
}
53+
} else {
54+
grate_init();
55+
}
56+
57+
int status;
58+
while (wait(&status) > 0) {
59+
printf("[Grate | geteuid] terminated, status: %d\n", status);
60+
grate_destroy();
61+
}
62+
63+
return 0;
64+
}
65+
66+
67+
/* Auto-generated from grate definition file */
68+

tools/magic.tmpl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
This is an auto-generated file. Do not modify.
3+
*/
4+
5+
#include <stdlib.h>
6+
#include <unistd.h>
7+
#include <copy_data_between_cages.h>
8+
#include <register_handler.h>
9+
#include <sys/stat.h>
10+
#include <sys/utsname.h>
11+
12+
#define thiscage getpid()
13+
14+
#define COPY(src, dst, len, type) \
15+
copy_data_between_cages(thiscage, (src).cage, \
16+
(src).value, (src).cage, \
17+
(dst).value, (dst).cage, \
18+
(len), (type))
19+
20+
typedef struct lvar_t {
21+
uint64_t value;
22+
uint64_t cage;
23+
} lvar_t;
24+
25+
struct generic_args {
26+
uint64_t cageid;
27+
lvar_t arg1;
28+
lvar_t arg2;
29+
lvar_t arg3;
30+
lvar_t arg4;
31+
lvar_t arg5;
32+
lvar_t arg6;
33+
};
34+
35+
// __attribute__((weak)) void grate_init(void);
36+
// __attribute__((weak)) void grate_destroy(void);
37+
38+
void grate_init(void);
39+
void grate_destroy(void);
40+
41+
void register_handlers(int, int);
42+
43+
/* Types get attached down below */

tools/run.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/bash
2+
3+
set -x
4+
5+
WASMTIME_BACKTRACE_DETAILS=1 RUST_BACKTRACE=1 /home/lind/lind-wasm/src/wasmtime/target/release/wasmtime run --allow-precompiled --wasi threads=y --wasi preview2=n output/geteuid_grate.wasm getegid_grate.wasm ./mash.wasm -c "etest.wasm & imfs_grate.wasm runopen.wasm & getgid_grate.wasm gidtest.wasm"

0 commit comments

Comments
 (0)