From 7fc4272b89d97d106f6e96bcc3e0267429541590 Mon Sep 17 00:00:00 2001 From: Ken Sato Date: Thu, 14 Nov 2019 10:56:28 +0900 Subject: [PATCH] handle execveat systemcall on McKernel Refs: #1366 Change-Id: I921e04a0df8d0d798fc94f675e5112dd2fec190a --- arch/arm64/kernel/include/syscall_list.h | 1 + arch/x86_64/kernel/include/syscall_list.h | 1 + executer/user/mcexec.c | 50 +++++++++++- kernel/include/syscall.h | 5 ++ kernel/syscall.c | 55 +++++++++++-- test/issues/1366/C1366.sh | 29 +++++++ test/issues/1366/Makefile | 11 +++ test/issues/1366/README | 24 ++++++ test/issues/1366/aarch64_result.log | 95 +++++++++++++++++++++++ test/issues/1366/x86_64_result.log | 95 +++++++++++++++++++++++ 10 files changed, 356 insertions(+), 10 deletions(-) create mode 100755 test/issues/1366/C1366.sh create mode 100644 test/issues/1366/Makefile create mode 100644 test/issues/1366/README create mode 100644 test/issues/1366/aarch64_result.log create mode 100644 test/issues/1366/x86_64_result.log diff --git a/arch/arm64/kernel/include/syscall_list.h b/arch/arm64/kernel/include/syscall_list.h index 5dd62433..fd868af7 100644 --- a/arch/arm64/kernel/include/syscall_list.h +++ b/arch/arm64/kernel/include/syscall_list.h @@ -119,6 +119,7 @@ SYSCALL_DELEGATED(241, perf_event_open) SYSCALL_HANDLED(260, wait4) SYSCALL_HANDLED(270, process_vm_readv) SYSCALL_HANDLED(271, process_vm_writev) +SYSCALL_HANDLED(281, execveat) SYSCALL_HANDLED(700, get_cpu_id) #ifdef PROFILE_ENABLE SYSCALL_HANDLED(__NR_profile, profile) diff --git a/arch/x86_64/kernel/include/syscall_list.h b/arch/x86_64/kernel/include/syscall_list.h index c27cbbb2..e0298446 100644 --- a/arch/x86_64/kernel/include/syscall_list.h +++ b/arch/x86_64/kernel/include/syscall_list.h @@ -163,6 +163,7 @@ SYSCALL_HANDLED(303, mod_call) SYSCALL_HANDLED(309, getcpu) SYSCALL_HANDLED(310, process_vm_readv) SYSCALL_HANDLED(311, process_vm_writev) +SYSCALL_HANDLED(322, execveat) SYSCALL_HANDLED(700, get_cpu_id) #ifdef PROFILE_ENABLE SYSCALL_HANDLED(__NR_profile, profile) diff --git a/executer/user/mcexec.c b/executer/user/mcexec.c index 7f84284a..837f8bcc 100644 --- a/executer/user/mcexec.c +++ b/executer/user/mcexec.c @@ -3521,6 +3521,39 @@ again: return ret; } +/* for execveat */ +static int getpath_execveat(int dirfd, const char *filename, int flags, + char *pathbuf, size_t size) +{ + int rc, ret = 0; + size_t len; + + if (filename[0] == '/' || dirfd == AT_FDCWD) { + len = snprintf(pathbuf, size, "%s", filename); + } + else if (flags & AT_EMPTY_PATH && filename[0] == '\0') { + len = snprintf(pathbuf, size, "/dev/fd/%d", dirfd); + } + else { + len = snprintf(pathbuf, size, "/dev/fd/%d/%s", dirfd, filename); + } + + if (len >= size) { + ret = ENAMETOOLONG; + goto out; + } + + if (flags & AT_SYMLINK_NOFOLLOW) { + if ((rc = readlink(filename, pathbuf, PATH_MAX)) != -1) { + ret = ELOOP; + goto out; + } + } + +out: + return ret; +} + int main_loop(struct thread_data_s *my_thread) { struct syscall_wait_desc w; @@ -3929,6 +3962,7 @@ fork_err: break; } + /* Actually, performing execveat() for McKernel */ case __NR_execve: { /* Execve phase */ @@ -3940,15 +3974,25 @@ fork_err: char *shebang_argv_flat; char *buffer; size_t size; - int ret; + int ret, dirfd, flags; /* Load descriptor phase */ case 1: shebang_argv = NULL; buffer = NULL; desc = NULL; - filename = (char *)w.sr.args[1]; + dirfd = (int)w.sr.args[1]; + filename = (char *)w.sr.args[2]; + flags = (int)w.sr.args[4]; + ret = getpath_execveat(dirfd, + filename, flags, + pathbuf, PATH_MAX); + if (ret) { + goto return_execve1; + } + filename = pathbuf; + if ((ret = load_elf_desc_shebang(filename, &desc, &shebang_argv, 0)) != 0) { goto return_execve1; @@ -3987,7 +4031,7 @@ fork_err: /* Copy descriptor to co-kernel side */ trans.userp = buffer; - trans.rphys = w.sr.args[2]; + trans.rphys = w.sr.args[3]; trans.size = size; trans.direction = MCEXEC_UP_TRANSFER_TO_REMOTE; diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 154205f5..73a865fe 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -122,6 +122,11 @@ # define CLONE_NEWNET 0x40000000 /* New network namespace. */ # define CLONE_IO 0x80000000 /* Clone I/O context. */ +/* for execveat() */ +#define AT_FDCWD -100 +#define AT_SYMLINK_NOFOLLOW 0x100 +#define AT_EMPTY_PATH 0x1000 + struct user_desc { unsigned int entry_number; unsigned int base_addr; diff --git a/kernel/syscall.c b/kernel/syscall.c index 1e628dd9..1d8bb29e 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -2493,13 +2493,11 @@ static void munmap_all(void) return; } /* munmap_all() */ -SYSCALL_DECLARE(execve) +static int do_execveat(ihk_mc_user_context_t *ctx, int dirfd, + const char *filename, char **argv, char **envp, int flags) { int error; long ret; - const char *filename = (const char *)ihk_mc_syscall_arg0(ctx); - char **argv = (char **)ihk_mc_syscall_arg1(ctx); - char **envp = (char **)ihk_mc_syscall_arg2(ctx); char *argv_flat = NULL; int argv_flat_len = 0; @@ -2538,8 +2536,10 @@ SYSCALL_DECLARE(execve) /* Request host to open executable and load ELF section descriptions */ request.number = __NR_execve; request.args[0] = 1; /* 1st phase - get ELF desc */ - request.args[1] = (unsigned long)filename; - request.args[2] = virt_to_phys(desc); + request.args[1] = dirfd; + request.args[2] = (unsigned long)filename; + request.args[3] = virt_to_phys(desc); + request.args[4] = flags; ret = do_syscall(&request, ihk_mc_get_processor_id()); if (ret != 0) { @@ -2626,7 +2626,7 @@ SYSCALL_DECLARE(execve) } /* Request host to transfer ELF image */ - request.number = __NR_execve; + request.number = __NR_execve; request.args[0] = 2; /* 2nd phase - transfer ELF image */ request.args[1] = virt_to_phys(desc); request.args[2] = sizeof(struct program_load_desc) + @@ -2687,6 +2687,14 @@ end: return ret; } +SYSCALL_DECLARE(execve) +{ + return do_execveat(ctx, AT_FDCWD, + (const char *)ihk_mc_syscall_arg0(ctx), + (char **)ihk_mc_syscall_arg1(ctx), + (char **)ihk_mc_syscall_arg2(ctx), 0); +} + unsigned long do_fork(int clone_flags, unsigned long newsp, unsigned long parent_tidptr, unsigned long child_tidptr, unsigned long tlsblock_base, unsigned long curpc, @@ -3569,6 +3577,39 @@ out: return rc; } +SYSCALL_DECLARE(execveat) +{ + int dirfd = (int)ihk_mc_syscall_arg0(ctx); + const char *filename = (const char *)ihk_mc_syscall_arg1(ctx); + int flags = (int)ihk_mc_syscall_arg4(ctx); + long ret; + + /* validate flags */ + if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) { + ret = -EINVAL; + goto out; + } + + if (filename[0] == '/' || dirfd == AT_FDCWD) { + /* behave same as execve */ + goto exec; + } + + /* validate dirfd */ + if (dirfd < 0 && dirfd != AT_FDCWD) { + ret = -EBADF; + goto out; + } + +exec: + ret = do_execveat(ctx, dirfd, filename, + (char **)ihk_mc_syscall_arg2(ctx), + (char **)ihk_mc_syscall_arg3(ctx), flags); + +out: + return ret; +} + SYSCALL_DECLARE(close) { int fd = ihk_mc_syscall_arg0(ctx); diff --git a/test/issues/1366/C1366.sh b/test/issues/1366/C1366.sh new file mode 100755 index 00000000..485df53e --- /dev/null +++ b/test/issues/1366/C1366.sh @@ -0,0 +1,29 @@ +#/bin/sh + +USELTP=1 +USEOSTEST=0 + +. ../../common.sh + +issue=1366 +tid=01 + +cp ${LTPBIN}/execve_* ./ +cp ${LTPBIN}/execveat_* ./ + +for tp in execveat01 execveat02 execveat03 execve01 execve02 execve03 execve05 +do + tname=`printf "C${issue}T%02d" ${tid}` + echo "*** ${tname} start *******************************" + sudo PATH=${LTPBIN}:${PATH} $MCEXEC $LTPBIN/$tp 2>&1 | tee $tp.txt + ok=`grep PASS $tp.txt | wc -l` + ng=`grep FAIL $tp.txt | wc -l` + if [ $ng = 0 ]; then + echo "*** ${tname} PASSED ($ok)" + else + echo "*** ${tname} FAILED (ok=$ok ng=%ng)" + fi + let tid++ + echo "" +done + diff --git a/test/issues/1366/Makefile b/test/issues/1366/Makefile new file mode 100644 index 00000000..09141472 --- /dev/null +++ b/test/issues/1366/Makefile @@ -0,0 +1,11 @@ +CFLAGS=-g +LDFLAGS= + +TARGET= + +all: $(TARGET) + +test: all + ./C1366.sh +clean: + rm -f $(TARGET) *.o *.txt execve_* execveat_* diff --git a/test/issues/1366/README b/test/issues/1366/README new file mode 100644 index 00000000..89b261ce --- /dev/null +++ b/test/issues/1366/README @@ -0,0 +1,24 @@ +【Issue#1366 動作確認】 +□ テスト内容 +1. 以下のLTPを用いてexecveat機能がMckernelで動作することを確認 + - execveat01 + - execveat02 + - execveat03 + +2. 以下のLTPを用いて既存のexecve機能に影響が無いことを確認 + - execve01 + - execve02 + - execve03 + - execve05 + +□ 実行手順 +$ make test + +McKernelのインストール先や、OSTEST, LTPの配置場所は、 +$HOME/.mck_test_config を参照している +.mck_test_config は、McKernelをビルドした際に生成されるmck_test_config.sample ファイルを +$HOMEにコピーし、適宜編集する + +□ 実行結果 +x86_64_result.log aarch64_result.log 参照。 +すべての項目をPASSしていることを確認。 diff --git a/test/issues/1366/aarch64_result.log b/test/issues/1366/aarch64_result.log new file mode 100644 index 00000000..4c63c940 --- /dev/null +++ b/test/issues/1366/aarch64_result.log @@ -0,0 +1,95 @@ +*** C1366T01 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected + +Summary: +passed 4 +failed 0 +skipped 0 +warnings 0 +*** C1366T01 PASSED (4) + +*** C1366T02 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat02.c:64: PASS: execveat() fails as expected: EBADF +execveat02.c:64: PASS: execveat() fails as expected: EINVAL +execveat02.c:64: PASS: execveat() fails as expected: ELOOP +execveat02.c:64: PASS: execveat() fails as expected: ENOTDIR + +Summary: +passed 4 +failed 0 +skipped 0 +warnings 0 +*** C1366T02 PASSED (4) + +*** C1366T03 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat_child.c:36: PASS: execveat_child run as expected + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T03 PASSED (1) + +*** C1366T04 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve01_child.c:46: PASS: execve01_child executed + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T04 PASSED (1) + +*** C1366T05 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve02.c:66: PASS: execve() failed expectedly: EACCES + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T05 PASSED (1) + +*** C1366T06 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve03.c:144: PASS: execve failed as expected: ENAMETOOLONG +execve03.c:144: PASS: execve failed as expected: ENOENT +execve03.c:144: PASS: execve failed as expected: ENOTDIR +execve03.c:144: PASS: execve failed as expected: EFAULT +execve03.c:144: PASS: execve failed as expected: EACCES +execve03.c:144: PASS: execve failed as expected: ENOEXEC + +Summary: +passed 6 +failed 0 +skipped 0 +warnings 0 +*** C1366T06 PASSED (6) + +*** C1366T07 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' + +Summary: +passed 8 +failed 0 +skipped 0 +warnings 0 +*** C1366T07 PASSED (8) + diff --git a/test/issues/1366/x86_64_result.log b/test/issues/1366/x86_64_result.log new file mode 100644 index 00000000..4c63c940 --- /dev/null +++ b/test/issues/1366/x86_64_result.log @@ -0,0 +1,95 @@ +*** C1366T01 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected +execveat_child.c:36: PASS: execveat_child run as expected + +Summary: +passed 4 +failed 0 +skipped 0 +warnings 0 +*** C1366T01 PASSED (4) + +*** C1366T02 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat02.c:64: PASS: execveat() fails as expected: EBADF +execveat02.c:64: PASS: execveat() fails as expected: EINVAL +execveat02.c:64: PASS: execveat() fails as expected: ELOOP +execveat02.c:64: PASS: execveat() fails as expected: ENOTDIR + +Summary: +passed 4 +failed 0 +skipped 0 +warnings 0 +*** C1366T02 PASSED (4) + +*** C1366T03 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execveat_child.c:36: PASS: execveat_child run as expected + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T03 PASSED (1) + +*** C1366T04 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve01_child.c:46: PASS: execve01_child executed + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T04 PASSED (1) + +*** C1366T05 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve02.c:66: PASS: execve() failed expectedly: EACCES + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C1366T05 PASSED (1) + +*** C1366T06 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve03.c:144: PASS: execve failed as expected: ENAMETOOLONG +execve03.c:144: PASS: execve failed as expected: ENOENT +execve03.c:144: PASS: execve failed as expected: ENOTDIR +execve03.c:144: PASS: execve failed as expected: EFAULT +execve03.c:144: PASS: execve failed as expected: EACCES +execve03.c:144: PASS: execve failed as expected: ENOEXEC + +Summary: +passed 6 +failed 0 +skipped 0 +warnings 0 +*** C1366T06 PASSED (6) + +*** C1366T07 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' +execve_child.c:34: PASS: argv[1] is canary, expected 'canary' + +Summary: +passed 8 +failed 0 +skipped 0 +warnings 0 +*** C1366T07 PASSED (8) +