diff --git a/doc/user-getting-started.md b/doc/user-getting-started.md index e257e40bb..cc12afcad 100644 --- a/doc/user-getting-started.md +++ b/doc/user-getting-started.md @@ -65,20 +65,43 @@ Options: multiplier suffix: k 1024, m 1024*1024, or g 1024*1024*1024. Ignored if smaller than the existing default thread stack size - --app-config-path -- specifies the configuration json file for - running an unsigned binary. The file can be - the same one used for the signing process. + --app-config-path + -- specifies the configuration json file for + running an unsigned binary. The file can be + the same one used for the signing process. --host-to-enc-uid-map -- comma separated list of uid mappings between - the host and the enclave + the host and the enclave --host-to-enc-gid-map -- comma separated list of gid mappings between - the host and the enclave + the host and the enclave --unhandled-syscall-enosys -- flag indicating if the app must exit when it encounters an unimplemented syscall 'true' implies the syscall would not terminate and instead return ENOSYS. + --strace + -- Use this option to display the system call traces of + the execution + --strace-failing + -- When specified, all syscalls that fail will be logged. + Other syscalls will not be logged, unless specified via + filter (see below). Set a breakpoint in _strace_failure_hook + to stop execution whenever a syscall fails. Use breakpoint + conditions to control the behavior of the breakpoint. + E.g: Use syscall number as a condition in the breakpoint + --strace-filter 'SYS_name1:SYS_name2:...' + -- Specify the set of syscalls to be traced. When filters + are specified, only those syscalls specified in the filter + will be traced, in addition to failing syscalls if + specified as described above. + E.g: To trace open and mprotect syscalls, specify + --strace-filter 'SYS_open:SYS_mprotect' + --strace-exclude-filter 'SYS_name1:SYS_name2:...' + -- Specify a set of syscalls to exclude from the strace log. + All other syscalls will be logged in the strace. + E.g: To exclude open and mprotect syscalls, specify + --strace-exclude-filter 'SYS_open:SYS_mprotect' ``` You can also specify many other parameters in a configuration file, conventionally called `config.json`, you will also use this configuration file to package your application. diff --git a/tests/Makefile b/tests/Makefile index 96efa7c43..948aa4843 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -136,6 +136,8 @@ endif DIRS += appenv DIRS += go +DIRS += strace + .PHONY: $(DIRS) diff --git a/tests/strace/.gitignore b/tests/strace/.gitignore new file mode 100644 index 000000000..f34d65968 --- /dev/null +++ b/tests/strace/.gitignore @@ -0,0 +1,7 @@ +1 +2 +3 +test1.txt +test2.txt +test3.txt +test4.txt \ No newline at end of file diff --git a/tests/strace/Makefile b/tests/strace/Makefile new file mode 100644 index 000000000..8a96f1cb9 --- /dev/null +++ b/tests/strace/Makefile @@ -0,0 +1,51 @@ +TOP=$(abspath ../..) +include $(TOP)/defs.mak + +APPDIR = appdir +CFLAGS = -fPIC +LDFLAGS = -Wl,-rpath=$(MUSL_LIB) + +all: + $(MAKE) myst + $(MAKE) rootfs + +rootfs: strace.c + mkdir -p $(APPDIR)/bin + $(CC) $(CFLAGS) -o $(APPDIR)/bin/strace strace.c $(LDFLAGS) + $(MYST) mkcpio $(APPDIR) rootfs + +OPTS = + +ifdef PERF +OPTS += --perf +endif + +OPTS += --thread-stack-size=1048576 + +tests: all test1 test2 test3 test4 + echo " === passed test strace" + +test1: + -$(MYST_EXEC) rootfs /bin/strace --strace-filter=SYS_invali $(OPTS) 2> test1.txt || $(RUNTEST) diff test1.txt test1.expected.txt + echo "=== test1 passed" + +test2: + -$(MYST_EXEC) rootfs /bin/strace --strace-filter=SYS_mmap --strace-exclude-filter=SYS_ioctl $(OPTS) 2> test2.txt || $(RUNTEST) diff test2.txt test2.expected.txt + echo "=== test2 passed" + +test3: + $(MYST_EXEC) rootfs /bin/strace --strace-filter=SYS_close:SYS_open $(OPTS) 2> test3.txt + cat test3.txt | grep "SYS_" > 1 && cat test3.txt | grep -E '(SYS_close|SYS_open)' > 2 && $(RUNTEST) diff 1 2 + echo "=== test3 passed" + +test4: + $(MYST_EXEC) rootfs /bin/strace --strace-exclude-filter 'SYS_open:SYS_close' $(OPTS) 2> test4.txt + cat test4.txt | grep -v "SYS_open" > 3 && $(RUNTEST) diff 3 test4.txt + cat test4.txt | grep -v "SYS_close" > 3 && $(RUNTEST) diff 3 test4.txt + echo "=== test4 passed" + +myst: + $(MAKE) -C $(TOP)/tools/myst + +clean: + rm -rf $(APPDIR) rootfs export ramfs 1 2 3 test1.txt text2.txt test3.txt test4.txt diff --git a/tests/strace/strace.c b/tests/strace/strace.c new file mode 100644 index 000000000..fbf3693d8 --- /dev/null +++ b/tests/strace/strace.c @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include +#include + +void foofoo() +{ +} + +int main(int argc, const char* argv[]) +{ + char buf[PATH_MAX]; + + foofoo(); + + getcwd(buf, sizeof(buf)); + assert(strcmp(buf, "/") == 0); + + return 0; +} diff --git a/tests/strace/test1.expected.txt b/tests/strace/test1.expected.txt new file mode 100644 index 000000000..d0f0e887e --- /dev/null +++ b/tests/strace/test1.expected.txt @@ -0,0 +1,2 @@ +Unknown syscall SYS_invali specified in --strace-filter or --strace-exclude-filter +Aborted (core dumped) diff --git a/tests/strace/test2.expected.txt b/tests/strace/test2.expected.txt new file mode 100644 index 000000000..8127ab8a1 --- /dev/null +++ b/tests/strace/test2.expected.txt @@ -0,0 +1,2 @@ +Cannot specify both --strace-filter and --strace-exclude-filter +Aborted (core dumped) diff --git a/tools/myst/host/exec.c b/tools/myst/host/exec.c index 12508eee5..e1a2a6abd 100644 --- a/tools/myst/host/exec.c +++ b/tools/myst/host/exec.c @@ -371,20 +371,43 @@ Options:\n\ main thread, where may have a\n\ multiplier suffix: k 1024, m 1024*1024, or\n\ g 1024*1024*1024\n\ - --app-config-path -- specifies the configuration json file for\n\ - running an unsigned binary. The file can be\n\ - the same one used for the signing process.\n\ + --app-config-path \n\ + -- specifies the configuration json file for\n\ + running an unsigned binary. The file can be\n\ + the same one used for the signing process.\n\ --host-to-enc-uid-map \n\ -- comma separated list of uid mappings between\n\ - the host and the enclave\n\ + the host and the enclave\n\ --host-to-enc-gid-map \n\ -- comma separated list of gid mappings between\n\ - the host and the enclave\n\ + the host and the enclave\n\ --unhandled-syscall-enosys \n\ -- flag indicating if the app must exit when\n\ it encounters an unimplemented syscall\n\ 'true' implies the syscall would not terminate\n\ and instead return ENOSYS.\n\ + --strace \n\ + -- Use this option to display the system call traces of \n\ + the execution\n\ + --strace-failing\n\ + -- When specified, all syscalls that fail will be logged.\n\ + Other syscalls will not be logged, unless specified via \n\ + filter (see below). Set a breakpoint in _strace_failure_hook \n\ + to stop execution whenever a syscall fails. Use breakpoint \n\ + conditions to control the behavior of the breakpoint.\n\ + E.g: Use syscall number as a condition in the breakpoint\n\ + --strace-filter 'SYS_name1:SYS_name2:...'\n\ + -- Specify the set of syscalls to be traced. When filters \n\ + are specified, only those syscalls specified in the filter \n\ + will be traced, in addition to failing syscalls if\n\ + specified as described above.\n\ + E.g: To trace open and mprotect syscalls, specify\n\ + --strace-filter 'SYS_open:SYS_mprotect'\n\ + --strace-exclude-filter 'SYS_name1:SYS_name2:...'\n\ + -- Specify a set of syscalls to exclude from the strace log. \n\ + All other syscalls will be logged in the strace. \n\ + E.g: To exclude open and mprotect syscalls, specify\n\ + --strace-exclude-filter 'SYS_open:SYS_mprotect'\n\ \n" int exec_action(int argc, const char* argv[], const char* envp[]) diff --git a/tools/myst/host/exec_linux.c b/tools/myst/host/exec_linux.c index 3587441ae..1c91c9a08 100644 --- a/tools/myst/host/exec_linux.c +++ b/tools/myst/host/exec_linux.c @@ -68,20 +68,43 @@ Options:\n\ main thread, where may have a\n\ multiplier suffix: k 1024, m 1024*1024, or\n\ g 1024*1024*1024\n\ - --app-config-path -- specifies the configuration json file for\n\ - running an unsigned binary. The file can be\n\ - the same one used for the signing process.\n\ + --app-config-path \n\ + -- specifies the configuration json file for\n\ + running an unsigned binary. The file can be\n\ + the same one used for the signing process.\n\ --host-to-enc-uid-map \n\ -- comma separated list of uid mappings between\n\ - the host and the enclave\n\ + the host and the enclave\n\ --host-to-enc-gid-map \n\ -- comma separated list of gid mappings between\n\ - the host and the enclave\n\ + the host and the enclave\n\ --unhandled-syscall-enosys \n\ -- flag indicating if the app must exit when\n\ it encounters an unimplemented syscall\n\ 'true' implies the syscall would not terminate\n\ and instead return ENOSYS.\n\ + --strace \n\ + -- Use this option to display the system call traces of \n\ + the execution\n\ + --strace-failing\n\ + -- When specified, all syscalls that fail will be logged.\n\ + Other syscalls will not be logged, unless specified via \n\ + filter (see below). Set a breakpoint in _strace_failure_hook \n\ + to stop execution whenever a syscall fails. Use breakpoint \n\ + conditions to control the behavior of the breakpoint.\n\ + E.g: Use syscall number as a condition in the breakpoint\n\ + --strace-filter 'SYS_name1:SYS_name2:...'\n\ + -- Specify the set of syscalls to be traced. When filters \n\ + are specified, only those syscalls specified in the filter \n\ + will be traced, in addition to failing syscalls if\n\ + specified as described above.\n\ + E.g: To trace open and mprotect syscalls, specify\n\ + --strace-filter 'SYS_open:SYS_mprotect'\n\ + --strace-exclude-filter 'SYS_name1:SYS_name2:...'\n\ + -- Specify a set of syscalls to exclude from the strace log. \n\ + All other syscalls will be logged in the strace. \n\ + E.g: To exclude open and mprotect syscalls, specify\n\ + --strace-exclude-filter 'SYS_open:SYS_mprotect'\n\ \n\ " diff --git a/tools/myst/host/strace.c b/tools/myst/host/strace.c index a6c962ddd..3a0407374 100644 --- a/tools/myst/host/strace.c +++ b/tools/myst/host/strace.c @@ -8,6 +8,49 @@ #include #include +int myst_set_strace_filter( + int num_tokens, + char** tokens, + myst_strace_config_t* strace_config, + bool include) +{ + for (size_t i = 0; i < MYST_MAX_SYSCALLS; ++i) + { + strace_config->trace[i] = !include; + } + + for (size_t i = 0; i < num_tokens; ++i) + { + const char* name = tokens[i]; + long num = myst_syscall_num(name); + if (num >= 0) + { + if (num < MYST_MAX_SYSCALLS) + strace_config->trace[num] = include; + else + { + fprintf( + stderr, + "Syscall %s exceeds trace array. Fix " + "myst_syscall_config_t\n", + name); + abort(); + } + } + else + { + fprintf( + stderr, + "Unknown syscall %s specified in --strace-filter or " + "--strace-exclude-filter \n", + name); + abort(); + } + } + strace_config->filter = 1; + return 0; +} + int myst_parse_strace_config( int* argc, const char** argv, @@ -17,6 +60,7 @@ int myst_parse_strace_config( const char* filter = NULL; char** tokens = NULL; size_t num_tokens = 0; + bool filter_flag = 0; if (cli_getopt(argc, argv, "--strace-failing", NULL) == 0) { @@ -31,35 +75,33 @@ int myst_parse_strace_config( fprintf(stderr, "Invalid strace-filter '%s' specified.\n", filter); abort(); } - for (size_t i = 0; i < num_tokens; ++i) + + ret = myst_set_strace_filter(num_tokens, tokens, strace_config, 1); + filter_flag = true; + } + + if (cli_getopt(argc, argv, "--strace-exclude-filter", &filter) == 0 && + filter) + { + if (filter_flag) { - const char* name = tokens[i]; - long num = myst_syscall_num(name); - if (num >= 0) - { - if (num < MYST_MAX_SYSCALLS) - strace_config->trace[num] = 1; - else - { - fprintf( - stderr, - "Syscall %s exceeds trace array. Fix " - "myst_syscall_config_t\n", - name); - abort(); - } - } - else - { - fprintf( - stderr, - "Unknown syscall %s specified in --strace=filter\n", - name); - abort(); - } + fprintf( + stderr, + "Cannot specify both --strace-filter and " + "--strace-exclude-filter\n"); + abort(); } - strace_config->filter = 1; - ret = 0; + + if (myst_strsplit(filter, ":", &tokens, &num_tokens) != 0) + { + fprintf( + stderr, + "Invalid strace-exclude-filter '%s' specified.\n", + filter); + abort(); + } + + ret = myst_set_strace_filter(num_tokens, tokens, strace_config, 0); } if (tokens)