From 18412616e1fba9d97dc096ff7fab40587988dbde Mon Sep 17 00:00:00 2001 From: Ken Sato Date: Fri, 26 Jul 2019 09:25:36 +0900 Subject: [PATCH] munmap: Change permission of VMA back to RWX on unmap Change-Id: Ic02098e7458dd8fa2961fb03dc32e37fb18c5dc5 Refs: #988 --- executer/kernel/mcctrl/syscall.c | 4 + test/issues/988/C988.c | 128 +++++++++++++++++++++++++++++ test/issues/988/C988.sh | 40 +++++++++ test/issues/988/Makefile | 11 +++ test/issues/988/README | 54 ++++++++++++ test/issues/988/aarch64_result.log | 71 ++++++++++++++++ test/issues/988/get_vma_prot.patch | 86 +++++++++++++++++++ test/issues/988/x86_64_result.log | 71 ++++++++++++++++ 8 files changed, 465 insertions(+) create mode 100644 test/issues/988/C988.c create mode 100755 test/issues/988/C988.sh create mode 100644 test/issues/988/Makefile create mode 100644 test/issues/988/README create mode 100644 test/issues/988/aarch64_result.log create mode 100644 test/issues/988/get_vma_prot.patch create mode 100644 test/issues/988/x86_64_result.log diff --git a/executer/kernel/mcctrl/syscall.c b/executer/kernel/mcctrl/syscall.c index 69ffd0f8..56dccf11 100644 --- a/executer/kernel/mcctrl/syscall.c +++ b/executer/kernel/mcctrl/syscall.c @@ -1860,6 +1860,8 @@ int mcctrl_clear_pte_range(uintptr_t start, uintptr_t len) } if (addr < end) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) + /* Revert permission */ + vma->vm_flags |= VM_READ | VM_WRITE | VM_EXEC; error = zap_vma_ptes(vma, addr, end-addr); if (error) { mcctrl_zap_page_range(vma, addr, end-addr, @@ -1877,6 +1879,8 @@ int mcctrl_clear_pte_range(uintptr_t start, uintptr_t len) NULL); } else { + /* Revert permission */ + vma->vm_flags |= VM_READ | VM_WRITE | VM_EXEC; zap_vma_ptes(vma, addr, end-addr); } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) */ diff --git a/test/issues/988/C988.c b/test/issues/988/C988.c new file mode 100644 index 00000000..9a23522d --- /dev/null +++ b/test/issues/988/C988.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include + +#define DEF_PROT (PROT_READ | PROT_WRITE | PROT_EXEC) + +int main(void) +{ + long pgsize = getpagesize(); + void *ptr1, *ptr2; + int rc, ret; + + printf("*** Check VMA's prot ***\n"); + + ptr1 = mmap(0, pgsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (ptr1 == MAP_FAILED) { + perror("mmap"); + ret = -1; + goto out; + } + memset(ptr1, '1', pgsize); + printf("** mmap %p: Succeed\n", ptr1); + + // Check default prot + rc = syscall(899, ptr1); + if (rc == PROT_READ | PROT_WRITE | PROT_EXEC) { + printf("[OK] default : %d\n", rc); + } + else { + printf("[NG] default : %d\n", rc); + ret = -1; + goto out; + } + + // mprotec PROT_WRITE | PROT_EXEC to ptr1 + rc = mprotect(ptr1, pgsize, PROT_NONE); + if (rc != 0) { + perror("mprotect"); + ret = -1; + goto out; + } + rc = mprotect(ptr1, pgsize, PROT_WRITE | PROT_EXEC); + if (rc != 0) { + perror("mprotect"); + ret = -1; + goto out; + } + printf("** mprotect PROT_WRITE | PROT_EXEC: Succeed\n"); + + // Check prot after mprotect + rc = syscall(899, ptr1); + if (rc == PROT_WRITE | PROT_EXEC) { + printf("[OK] after mprotect : %d\n", rc); + } + else { + printf("[NG] after mprotect : %d\n", rc); + ret = -1; + goto out; + } + + munmap(ptr1, pgsize); + printf("** munmap %p: Done\n", ptr1); + + // Check after munmap + rc = syscall(899, ptr1); + if (rc == DEF_PROT) { + printf("[OK] after munmap : %d\n", rc); + } + else { + printf("[NG] after munmap : %d\n", rc); + ret = -1; + goto out; + } + + ptr2 = mmap(0, pgsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (ptr2 == MAP_FAILED) { + perror("mmap"); + ret = -1; + goto out; + } + memset(ptr2, '2', pgsize); + printf("** mmap %p: Succeed\n", ptr2); + + // mprotec PROT_NONE to ptr2 + rc = mprotect(ptr2, pgsize, PROT_NONE); + if (rc != 0) { + perror("mprotect"); + ret = -1; + goto out; + } + printf("** mprotect PROT_NONE: Succeed\n"); + + // Check prot after mprotect + rc = syscall(899, ptr2); + if (rc != DEF_PROT) { + printf("[OK] after mprotect : %d\n", rc); + } + else { + printf("[NG] after mprotect : %d\n", rc); + ret = -1; + goto out; + } + munmap(ptr2, pgsize); + + // Check after munmap + rc = syscall(899, ptr2); + if (rc == DEF_PROT) { + printf("[OK] after munmap : %d\n", rc); + } + else { + printf("[NG] after munmap : %d\n", rc); + ret = -1; + goto out; + } + +out: + if (ret != 0) { + printf("TEST_FAILED\n"); + } + + return ret; +} diff --git a/test/issues/988/C988.sh b/test/issues/988/C988.sh new file mode 100755 index 00000000..4fbb4c83 --- /dev/null +++ b/test/issues/988/C988.sh @@ -0,0 +1,40 @@ +#/bin/sh + +USELTP=1 +USEOSTEST=0 + +. ../../common.sh + +issue=988 +tid=01 + +tname=`printf "C${issue}T%02d" ${tid}` +echo "*** ${tname} start *******************************" +ng=0 +${MCEXEC} ./C988 + +if [ $? -eq 0 ]; then + echo "*** ${tname} PASSED ******************************" +else + echo "*** ${tname} FAILED ******************************" +fi +let tid++ +echo "" + +for tp in mmap01 mmap02 mmap03 mmap04 mmap05 mmap06 mmap07 mmap08\ + mmap09 mmap12 mmap14 mmap15 +do + tname=`printf "C${issue}T%02d" ${tid}` + echo "*** ${tname} start *******************************" + sudo $MCEXEC $LTPBIN/$tp 2>&1 | tee $tp.txt + ok=`grep TPASS $tp.txt | wc -l` + ng=`grep TFAIL $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/988/Makefile b/test/issues/988/Makefile new file mode 100644 index 00000000..8abfc888 --- /dev/null +++ b/test/issues/988/Makefile @@ -0,0 +1,11 @@ +CFLAGS=-g +LDFLAGS= + +TARGET=C988 + +all: $(TARGET) + +test: all + ./C988.sh +clean: + rm -f $(TARGET) *.o *.txt diff --git a/test/issues/988/README b/test/issues/988/README new file mode 100644 index 00000000..1b63757c --- /dev/null +++ b/test/issues/988/README @@ -0,0 +1,54 @@ +【Issue#988 動作確認】 +□ 前提 + ・本Issueで修正したmcctrl_clear_pte_range() は、WRITE権限を持つmmap領域を + unmapした場合に呼び出される + なお、WRITE権限を持たない領域のunmapの際には、set_host_vma()によって + パーミッションの初期化が行われる + ・McKernelでは、PROT_WRITEを変更しないmprotectを行った場合、 + ホスト側のVMAの権限は変更されない + +□ テスト内容 +1. unmapしたホスト側のVMAの権限がRWX(デフォルト)になっていることを確認 +C988T01: + 以下の流れでmmap, mprotect, munmapを行い、munmap後のホスト側のVMAの + パーミッションを確認する + 1. mmapでメモリ領域を確保 + 2. 確保した領域のVMAの権限がRWXであることを確認 + 3. 確保した領域にmprotect(PROT_NONE) -> mprotect(PROT_WRITE | PROT_EXEC)を行う + (McKernelではWRITE属性を変更しないmprotectはvmaを変更しないため、一度NONEを設定する) + 4. 確保した領域のVMAの権限が-WXであることを確認 + 5. 確保した領域をmunmapで解放する + 6. 解放後の領域のVMAの権限がRWXであることを確認 + 7. mmapで再びメモリ領域を確保 + 8. 確保した領域にmprotect(PROT_NONE)を行う + 9. 確保した領域のVMAの権限がRWXでないことを確認 + 10. 確保した領域をmunmapで解放する + 11. 解放後の領域のVMAの権限がRWXであることを確認 + +2. 以下のLTPを用いて既存のmmap機能に影響が無いことを確認 + - mmap01 + - mmap02 + - mmap03 + - mmap04 + - mmap05 + - mmap06 + - mmap07 + - mmap08 + - mmap09 + - mmap12 + - mmap14 + - mmap15 + +□ 実行手順 +(1) cd && patch -p0 < /test/issues/988/get_vma_prot.patch +(2) McKernelをビルドする +(3) cd /test/issues/988/ && make test + +McKernelのインストール先や、OSTEST, LTPの配置場所は、 +$HOME/.mck_test_config を参照している +.mck_test_config は、McKernelをビルドした際に生成されるmck_test_config.sample ファイルを +$HOMEにコピーし、適宜編集する + +□ 実行結果 +result_x86_64.log および result_aarch64.log 参照。 +すべての項目をPASSしていることを確認。 diff --git a/test/issues/988/aarch64_result.log b/test/issues/988/aarch64_result.log new file mode 100644 index 00000000..2b378004 --- /dev/null +++ b/test/issues/988/aarch64_result.log @@ -0,0 +1,71 @@ +*** C988T01 start ******************************* +*** Check VMA's prot *** +** mmap 0x1000001e0000: Succeed +[OK] default : 7 +** mprotect PROT_WRITE | PROT_EXEC: Succeed +[OK] after mprotect : 6 +** munmap 0x1000001e0000: Done +[OK] after munmap : 7 +** mmap 0x1000001f0000: Succeed +** mprotect PROT_NONE: Succeed +[OK] after mprotect : 4 +[OK] after munmap : 7 +*** C988T01 PASSED ****************************** + +*** C988T02 start ******************************* +mmap01 1 TPASS : Functionality of mmap() successful +*** C988T02 PASSED (1) + +*** C988T03 start ******************************* +mmap02 1 TPASS : Functionality of mmap() successful +*** C988T03 PASSED (1) + +*** C988T04 start ******************************* +mmap03 1 TPASS : mmap() functionality is correct +*** C988T04 PASSED (1) + +*** C988T05 start ******************************* +mmap04 1 TPASS : Functionality of mmap() successful +*** C988T05 PASSED (1) + +*** C988T06 start ******************************* +mmap05 1 TPASS : Got SIGSEGV as expected +*** C988T06 PASSED (1) + +*** C988T07 start ******************************* +mmap06 1 TPASS : mmap failed with EACCES +*** C988T07 PASSED (1) + +*** C988T08 start ******************************* +mmap07 1 TPASS : mmap failed with EACCES +*** C988T08 PASSED (1) + +*** C988T09 start ******************************* +mmap08 1 TPASS : mmap failed with EBADF +*** C988T09 PASSED (1) + +*** C988T10 start ******************************* +mmap09 1 TPASS : ftruncate mmaped file to a smaller size +mmap09 2 TPASS : ftruncate mmaped file to a larger size +mmap09 3 TPASS : ftruncate mmaped file to 0 size +*** C988T10 PASSED (3) + +*** C988T11 start ******************************* +tst_test.c:1096: INFO: Timeout per run is 0h 05m 00s +mmap12.c:103: INFO: All pages are present +mmap12.c:127: PASS: File mapped properly + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C988T11 PASSED (0) + +*** C988T12 start ******************************* +mmap14 1 TPASS : Functionality of mmap() successful +*** C988T12 PASSED (1) + +*** C988T13 start ******************************* +mmap15 1 TPASS : mmap into high region failed as expected: errno=ENOMEM(12): Cannot allocate memory +*** C988T13 PASSED (1) diff --git a/test/issues/988/get_vma_prot.patch b/test/issues/988/get_vma_prot.patch new file mode 100644 index 00000000..3fcd264b --- /dev/null +++ b/test/issues/988/get_vma_prot.patch @@ -0,0 +1,86 @@ +diff --git arch/arm64/kernel/include/syscall_list.h arch/arm64/kernel/include/syscall_list.h +index 5dd6243..cef0005 100644 +--- arch/arm64/kernel/include/syscall_list.h ++++ arch/arm64/kernel/include/syscall_list.h +@@ -134,6 +134,7 @@ SYSCALL_HANDLED(802, linux_mlock) + SYSCALL_HANDLED(803, suspend_threads) + SYSCALL_HANDLED(804, resume_threads) + SYSCALL_HANDLED(811, linux_spawn) ++SYSCALL_HANDLED(899, get_vma_prot) + + SYSCALL_DELEGATED(1024, open) + SYSCALL_DELEGATED(1035, readlink) +diff --git arch/x86_64/kernel/include/syscall_list.h arch/x86_64/kernel/include/syscall_list.h +index 8ef9bd0..f130dbb 100644 +--- arch/x86_64/kernel/include/syscall_list.h ++++ arch/x86_64/kernel/include/syscall_list.h +@@ -176,4 +176,5 @@ SYSCALL_HANDLED(802, linux_mlock) + SYSCALL_HANDLED(803, suspend_threads) + SYSCALL_HANDLED(804, resume_threads) + SYSCALL_HANDLED(811, linux_spawn) ++SYSCALL_HANDLED(899, get_vma_prot) + /**** End of File ****/ +diff --git executer/kernel/mcctrl/syscall.c executer/kernel/mcctrl/syscall.c +index d742875..02fc0ef 100644 +--- executer/kernel/mcctrl/syscall.c ++++ executer/kernel/mcctrl/syscall.c +@@ -1777,6 +1777,28 @@ void __return_syscall(ihk_os_t os, struct ikc_scd_packet *packet, + ihk_device_unmap_memory(ihk_os_to_dev(os), phys, sizeof(*res)); + } + ++static int get_vma_prot(uintptr_t rva) ++{ ++ struct mm_struct *mm = current->mm; ++ struct vm_area_struct *vma; ++ int prot; ++ ++ down_write(&mm->mmap_sem); ++ vma = find_vma(mm, rva); ++ if (!vma || (rva < vma->vm_start)) { ++ printk("%s: find_vma failed.\n", __func__); ++ prot = -1; ++ goto out; ++ } ++ ++ prot = (int)(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)); ++ ++out: ++ up_write(&mm->mmap_sem); ++ ++ return prot; ++} ++ + static int remap_user_space(uintptr_t rva, size_t len, int prot) + { + struct mm_struct *mm = current->mm; +@@ -2080,6 +2102,10 @@ int __do_in_kernel_syscall(ihk_os_t os, struct ikc_scd_packet *packet) + + dprintk("%s: system call: %lx\n", __FUNCTION__, sc->args[0]); + switch (sc->number) { ++ case 899: ++ ret = get_vma_prot(sc->args[0]); ++ break; ++ + case __NR_mmap: + ret = pager_call(os, sc); + break; +diff --git kernel/syscall.c kernel/syscall.c +index 6517f84..a27c6b6 100644 +--- kernel/syscall.c ++++ kernel/syscall.c +@@ -2021,6 +2021,15 @@ out: + return error; + } + ++SYSCALL_DECLARE(get_vma_prot) ++{ ++ ihk_mc_user_context_t ctx0; ++ const unsigned long vaddr = ihk_mc_syscall_arg0(ctx); ++ ++ ihk_mc_syscall_arg0(&ctx0) = vaddr; ++ return syscall_generic_forwarding(899, &ctx0); ++} ++ + SYSCALL_DECLARE(mprotect) + { + const intptr_t start = ihk_mc_syscall_arg0(ctx); diff --git a/test/issues/988/x86_64_result.log b/test/issues/988/x86_64_result.log new file mode 100644 index 00000000..47ff4290 --- /dev/null +++ b/test/issues/988/x86_64_result.log @@ -0,0 +1,71 @@ +*** C988T01 start ******************************* +*** Check VMA's prot *** +** mmap 0x2aaaaadea000: Succeed +[OK] default : 7 +** mprotect PROT_WRITE | PROT_EXEC: Succeed +[OK] after mprotect : 6 +** munmap 0x2aaaaadea000: Done +[OK] after munmap : 7 +** mmap 0x2aaaaadeb000: Succeed +** mprotect PROT_WRITE: Succeed +[OK] after mprotect : 0 +[OK] after munmap : 7 +*** C988T01 PASSED ****************************** + +*** C988T02 start ******************************* +mmap01 1 TPASS : Functionality of mmap() successful +*** C988T02 PASSED (1) + +*** C988T03 start ******************************* +mmap02 1 TPASS : Functionality of mmap() successful +*** C988T03 PASSED (1) + +*** C988T04 start ******************************* +mmap03 1 TPASS : mmap() functionality is correct +*** C988T04 PASSED (1) + +*** C988T05 start ******************************* +mmap04 1 TPASS : Functionality of mmap() successful +*** C988T05 PASSED (1) + +*** C988T06 start ******************************* +mmap05 1 TPASS : Got SIGSEGV as expected +*** C988T06 PASSED (1) + +*** C988T07 start ******************************* +mmap06 1 TPASS : mmap failed with EACCES +*** C988T07 PASSED (1) + +*** C988T08 start ******************************* +mmap07 1 TPASS : mmap failed with EACCES +*** C988T08 PASSED (1) + +*** C988T09 start ******************************* +mmap08 1 TPASS : mmap failed with EBADF +*** C988T09 PASSED (1) + +*** C988T10 start ******************************* +mmap09 1 TPASS : ftruncate mmaped file to a smaller size +mmap09 2 TPASS : ftruncate mmaped file to a larger size +mmap09 3 TPASS : ftruncate mmaped file to 0 size +*** C988T10 PASSED (3) + +*** C988T11 start ******************************* +tst_test.c:934: INFO: Timeout per run is 0h 05m 00s +mmap12.c:103: INFO: All pages are present +mmap12.c:127: PASS: File mapped properly + +Summary: +passed 1 +failed 0 +skipped 0 +warnings 0 +*** C988T11 PASSED (0) + +*** C988T12 start ******************************* +mmap14 1 TPASS : Functionality of mmap() successful +*** C988T12 PASSED (1) + +*** C988T13 start ******************************* +mmap15 1 TPASS : mmap into high region failed as expected: errno=ENOMEM(12): Cannot allocate memory +*** C988T13 PASSED (1)