| /* |
| * Copyright (C) 2017 Red Hat, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| /* |
| * Based on Linux/tools/testing/selftests/memfd/memfd_test.c |
| * by David Herrmann <dh.herrmann@gmail.com> |
| * |
| * 24/02/2017 Port to LTP <jracek@redhat.com> |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #include "tst_test.h" |
| #include "memfd_create_common.h" |
| |
| /* |
| * Do few basic sealing tests to see whether setting/retrieving seals works. |
| */ |
| static void test_basic(int fd) |
| { |
| /* add basic seals */ |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SHRINK | F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK | F_SEAL_WRITE); |
| |
| /* add them again */ |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SHRINK | F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK | F_SEAL_WRITE); |
| |
| /* add more seals and seal against sealing */ |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_GROW | F_SEAL_SEAL); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK | F_SEAL_GROW | |
| F_SEAL_WRITE | F_SEAL_SEAL); |
| |
| /* verify that sealing no longer works */ |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_GROW); |
| CHECK_MFD_FAIL_ADD_SEALS(fd, 0); |
| } |
| |
| /* |
| * Verify that no sealing is possible when memfd is created without |
| * MFD_ALLOW_SEALING flag. |
| */ |
| static void test_no_sealing_without_flag(int fd) |
| { |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SEAL); |
| CHECK_MFD_FAIL_ADD_SEALS(fd, |
| F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SEAL); |
| } |
| |
| /* |
| * Test SEAL_WRITE |
| * Test whether SEAL_WRITE actually prevents modifications. |
| */ |
| static void test_seal_write(int fd) |
| { |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE); |
| |
| CHECK_MFD_READABLE(fd); |
| CHECK_MFD_NON_WRITEABLE(fd); |
| CHECK_MFD_SHRINKABLE(fd); |
| CHECK_MFD_GROWABLE(fd); |
| CHECK_MFD_NON_GROWABLE_BY_WRITE(fd); |
| } |
| |
| /* |
| * Test SEAL_SHRINK |
| * Test whether SEAL_SHRINK actually prevents shrinking |
| */ |
| static void test_seal_shrink(int fd) |
| { |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK); |
| |
| CHECK_MFD_READABLE(fd); |
| CHECK_MFD_WRITEABLE(fd); |
| CHECK_MFD_NON_SHRINKABLE(fd); |
| CHECK_MFD_GROWABLE(fd); |
| CHECK_MFD_GROWABLE_BY_WRITE(fd); |
| } |
| |
| /* |
| * Test SEAL_GROW |
| * Test whether SEAL_GROW actually prevents growing |
| */ |
| static void test_seal_grow(int fd) |
| { |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_GROW); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_GROW); |
| |
| CHECK_MFD_READABLE(fd); |
| CHECK_MFD_WRITEABLE(fd); |
| CHECK_MFD_SHRINKABLE(fd); |
| CHECK_MFD_NON_GROWABLE(fd); |
| CHECK_MFD_NON_GROWABLE_BY_WRITE(fd); |
| } |
| |
| /* |
| * Test SEAL_SHRINK | SEAL_GROW |
| * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing |
| */ |
| static void test_seal_resize(int fd) |
| { |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SHRINK | F_SEAL_GROW); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK | F_SEAL_GROW); |
| |
| CHECK_MFD_READABLE(fd); |
| CHECK_MFD_WRITEABLE(fd); |
| CHECK_MFD_NON_SHRINKABLE(fd); |
| CHECK_MFD_NON_GROWABLE(fd); |
| CHECK_MFD_NON_GROWABLE_BY_WRITE(fd); |
| } |
| |
| /* |
| * Test sharing via dup() |
| * Test that seals are shared between dupped FDs and they're all equal. |
| */ |
| static void test_share_dup(int fd) |
| { |
| int fd2; |
| |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| |
| fd2 = SAFE_DUP(fd); |
| CHECK_MFD_HAS_SEALS(fd2, 0); |
| |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE); |
| |
| CHECK_MFD_ADD_SEALS(fd2, F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE | F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); |
| |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SEAL); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); |
| |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_GROW); |
| CHECK_MFD_FAIL_ADD_SEALS(fd2, F_SEAL_GROW); |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_SEAL); |
| CHECK_MFD_FAIL_ADD_SEALS(fd2, F_SEAL_SEAL); |
| |
| SAFE_CLOSE(fd2); |
| |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_GROW); |
| } |
| |
| /* |
| * Test sealing with active mmap()s |
| * Modifying seals is only allowed if no other mmap() refs exist. |
| */ |
| static void test_share_mmap(int fd) |
| { |
| void *p; |
| |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| |
| /* shared/writable ref prevents sealing WRITE, but allows others */ |
| p = SAFE_MMAP(NULL, MFD_DEF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, |
| fd, 0); |
| |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_SHRINK); |
| SAFE_MUNMAP(p, MFD_DEF_SIZE); |
| |
| /* readable ref allows sealing */ |
| p = SAFE_MMAP(NULL, MFD_DEF_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE | F_SEAL_SHRINK); |
| SAFE_MUNMAP(p, MFD_DEF_SIZE); |
| } |
| |
| /* |
| * Test sealing with open(/proc/self/fd/%d) |
| * Via /proc we can get access to a separate file-context for the same memfd. |
| * This is *not* like dup(), but like a real separate open(). Make sure the |
| * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR. |
| */ |
| static void test_share_open(int fd) |
| { |
| int fd2; |
| |
| CHECK_MFD_HAS_SEALS(fd, 0); |
| |
| fd2 = CHECK_MFD_OPEN(fd, O_RDWR, 0); |
| CHECK_MFD_ADD_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE); |
| |
| CHECK_MFD_ADD_SEALS(fd2, F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE | F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); |
| |
| SAFE_CLOSE(fd); |
| fd = CHECK_MFD_OPEN(fd2, O_RDONLY, 0); |
| |
| CHECK_MFD_FAIL_ADD_SEALS(fd, F_SEAL_SEAL); |
| CHECK_MFD_HAS_SEALS(fd, F_SEAL_WRITE | F_SEAL_SHRINK); |
| CHECK_MFD_HAS_SEALS(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); |
| |
| SAFE_CLOSE(fd2); |
| } |
| |
| |
| static const struct tcase { |
| int flags; |
| void (*func)(int fd); |
| const char *desc; |
| } tcases[] = { |
| {MFD_ALLOW_SEALING, &test_basic, "Basic tests + set/get seals"}, |
| {0, &test_no_sealing_without_flag, "Disabled sealing"}, |
| |
| {MFD_ALLOW_SEALING, &test_seal_write, "Write seal"}, |
| {MFD_ALLOW_SEALING, &test_seal_shrink, "Shrink seal"}, |
| {MFD_ALLOW_SEALING, &test_seal_grow, "Grow seal"}, |
| {MFD_ALLOW_SEALING, &test_seal_resize, "Resize seal"}, |
| |
| {MFD_ALLOW_SEALING, &test_share_dup, "Seals shared for dup"}, |
| {MFD_ALLOW_SEALING, &test_share_mmap, "Seals shared for mmap"}, |
| {MFD_ALLOW_SEALING, &test_share_open, "Seals shared for open"}, |
| }; |
| |
| static void verify_memfd_create(unsigned int n) |
| { |
| int fd; |
| const struct tcase *tc; |
| |
| tc = &tcases[n]; |
| |
| tst_res(TINFO, "%s", tc->desc); |
| |
| fd = CHECK_MFD_NEW(TCID, MFD_DEF_SIZE, tc->flags); |
| |
| tc->func(fd); |
| |
| SAFE_CLOSE(fd); |
| } |
| |
| static void setup(void) |
| { |
| ASSERT_HAVE_MEMFD_CREATE(); |
| } |
| |
| static struct tst_test test = { |
| .tid = "memfd_create01", |
| .test = verify_memfd_create, |
| .tcnt = ARRAY_SIZE(tcases), |
| .setup = setup, |
| }; |