[LTP] [PATCH v2 5/6] syscalls: added memfd_create dir and memfd_create/memfd_create01.c
Cyril Hrubis
chrubis@suse.cz
Tue Mar 7 13:17:56 CET 2017
Hi!
> +/*
> + * Do few basic sealing tests to see whether setting/retrieving seals works.
> + */
> +static void test_basic(int fd)
> +{
> + /* add basic seals */
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_WRITE));
> +
> + /* add them again */
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_WRITE));
> +
> + /* add more seals and seal against sealing */
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_GROW |
> + F_SEAL_WRITE |
> + F_SEAL_SEAL));
> +
> + /* verify that sealing no longer works */
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_GROW));
> + CHECK_RESULT(mfd_fail_add_seals(fd, 0));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * 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_RESULT(mfd_assert_has_seals(fd, F_SEAL_SEAL));
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_SHRINK |
> + F_SEAL_GROW |
> + F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SEAL));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * Test SEAL_WRITE
> + * Test whether SEAL_WRITE actually prevents modifications.
> + */
> +static void test_seal_write(int fd)
> +{
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE));
> +
> + CHECK_RESULT(mfd_assert_read(fd));
> + CHECK_RESULT(mfd_fail_write(fd));
> + CHECK_RESULT(mfd_assert_shrink(fd));
> + CHECK_RESULT(mfd_assert_grow(fd));
> + CHECK_RESULT(mfd_fail_grow_write(fd));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * Test SEAL_SHRINK
> + * Test whether SEAL_SHRINK actually prevents shrinking
> + */
> +static void test_seal_shrink(int fd)
> +{
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SHRINK));
> +
> + CHECK_RESULT(mfd_assert_read(fd));
> + CHECK_RESULT(mfd_assert_write(fd));
> + CHECK_RESULT(mfd_fail_shrink(fd));
> + CHECK_RESULT(mfd_assert_grow(fd));
> + CHECK_RESULT(mfd_assert_grow_write(fd));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * Test SEAL_GROW
> + * Test whether SEAL_GROW actually prevents growing
> + */
> +static void test_seal_grow(int fd)
> +{
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_GROW));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_GROW));
> +
> + CHECK_RESULT(mfd_assert_read(fd));
> + CHECK_RESULT(mfd_assert_write(fd));
> + CHECK_RESULT(mfd_assert_shrink(fd));
> + CHECK_RESULT(mfd_fail_grow(fd));
> + CHECK_RESULT(mfd_fail_grow_write(fd));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * Test SEAL_SHRINK | SEAL_GROW
> + * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing
> + */
> +static void test_seal_resize(int fd)
> +{
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW));
> +
> + CHECK_RESULT(mfd_assert_read(fd));
> + CHECK_RESULT(mfd_assert_write(fd));
> + CHECK_RESULT(mfd_fail_shrink(fd));
> + CHECK_RESULT(mfd_fail_grow(fd));
> + CHECK_RESULT(mfd_fail_grow_write(fd));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * 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_RESULT(mfd_assert_has_seals(fd, 0));
> +
> + fd2 = SAFE_DUP(fd);
> + CHECK_RESULT(mfd_assert_has_seals(fd2, 0));
> +
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd2, F_SEAL_WRITE));
> +
> + CHECK_RESULT(mfd_assert_add_seals(fd2, F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK));
> +
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SEAL));
> + CHECK_RESULT(mfd_assert_has_seals(fd,
> + F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL));
> + CHECK_RESULT(mfd_assert_has_seals(fd2,
> + F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL));
> +
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_GROW));
> + CHECK_RESULT(mfd_fail_add_seals(fd2, F_SEAL_GROW));
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_SEAL));
> + CHECK_RESULT(mfd_fail_add_seals(fd2, F_SEAL_SEAL));
> +
> + SAFE_CLOSE(fd2);
> +
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_GROW));
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * 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_RESULT(mfd_assert_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_RESULT(mfd_fail_add_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_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_RESULT(mfd_assert_add_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK));
> + SAFE_MUNMAP(p, MFD_DEF_SIZE);
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +/*
> + * 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_RESULT(mfd_assert_has_seals(fd, 0));
> +
> + fd2 = CHECK_RESULT(mfd_assert_open(fd, O_RDWR, 0));
> + CHECK_RESULT(mfd_assert_add_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE));
> + CHECK_RESULT(mfd_assert_has_seals(fd2, F_SEAL_WRITE));
> +
> + CHECK_RESULT(mfd_assert_add_seals(fd2, F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK));
> +
> + SAFE_CLOSE(fd);
> + fd = CHECK_RESULT(mfd_assert_open(fd2, O_RDONLY, 0));
> +
> + CHECK_RESULT(mfd_fail_add_seals(fd, F_SEAL_SEAL));
> + CHECK_RESULT(mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK));
> + CHECK_RESULT(mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK));
> +
> + SAFE_CLOSE(fd2);
> +
> + tst_res(TPASS, "%s", __func__);
> +}
> +
> +static const struct tcase {
> + int flags;
> + void (*func)(int fd);
> +} tcases[] = {
> + {MFD_ALLOW_SEALING, &test_basic},
> + {0, &test_no_sealing_without_flag},
> +
> + {MFD_ALLOW_SEALING, &test_seal_write},
> + {MFD_ALLOW_SEALING, &test_seal_shrink},
> + {MFD_ALLOW_SEALING, &test_seal_grow},
> + {MFD_ALLOW_SEALING, &test_seal_resize},
> +
> + {MFD_ALLOW_SEALING, &test_share_dup},
> + {MFD_ALLOW_SEALING, &test_share_mmap},
> + {MFD_ALLOW_SEALING, &test_share_open},
> +};
> +
> +static void verify_memfd_create(unsigned int n)
> +{
> + const struct tcase *tc;
> +
> + tc = &tcases[n];
> +
> + TEST(mfd_assert_new(TCID, MFD_DEF_SIZE, tc->flags));
> + if (TEST_RETURN < 0)
> + tst_brk(TBROK | TERRNO, "memfd_create() failed unexpectedly");
> +
> + tc->func(TEST_RETURN);
> +
> + if (TEST_RETURN > 0)
> + SAFE_CLOSE(TEST_RETURN);
> +}
> +
> +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,
> +};
> diff --git a/testcases/kernel/syscalls/memfd_create/memfd_create_common.h b/testcases/kernel/syscalls/memfd_create/memfd_create_common.h
> new file mode 100644
> index 0000000..f07cc8c
> --- /dev/null
> +++ b/testcases/kernel/syscalls/memfd_create/memfd_create_common.h
> @@ -0,0 +1,525 @@
> +/*
> + * 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.
> + *
> + */
> +
> +#ifndef MEMFD_TEST_COMMON
> +#define MEMFD_TEST_COMMON
> +
> +#include <sys/types.h>
> +#include <sys/syscall.h>
> +#include <sys/uio.h>
> +#include <lapi/fallocate.h>
> +#include <lapi/fcntl.h>
> +#include <lapi/memfd.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +
> +#define TST_NO_DEFAULT_MAIN
> +#include <tst_test.h>
> +
> +#include "linux_syscall_numbers.h"
> +
> +#define MFD_DEF_SIZE 8192
> +#define STACK_SIZE 65536
> +
> +enum assert_retval {
> + ALL_OK,
> + MEMFD_FAIL,
> + MEMFD_MISMATCH,
> + FTRUNCATE_FAIL,
> + FCNTL_FAIL,
> + GET_SEALS_MISMATCH,
> + ADD_SEALS_FAIL,
> + ADD_SEALS_MISMATCH,
> + FSTAT_FAIL,
> + FILE_SIZE_MISMATCH,
> + OPEN_MISMATCH,
> + OPEN_FAIL,
> + MPROTECT_FAIL,
> + FALLOCATE_PUNCH_FAIL,
> + WRITE_FAIL,
> + WRITE_MISMATCH,
> + MUNMAP_FAIL,
> + MMAP_FAIL,
> + MMAP_MISMATCH,
> + MMAP_MPROT_MISMATCH,
> + FALLOCATE_PUNCH_MISMATCH,
> + FTRUNCATE_SHRINK_MISMATCH,
> + FALLOCATE_ALLOC_FAIL,
> + FTRUNCATE_GROW_MISMATCH,
> + FALLOCATE_ALLOC_MISMATCH,
> + PWRITE_FAIL,
> + PWRITE_MISMATCH,
> + READ_FAIL,
> + READ_MISMATCH,
> + CLOSE_FAIL,
> +};
> +
> +const char *err_msg[] = {
> + "all OK",
> + "memfd_create() failed",
> + "memfd_create() succeeded, but failure expected",
> + "ftruncate() failed",
> + "fcntl() failed",
> + "GET_SEALS() returned unexpected value",
> + "ADD_SEALS() failed",
> + "ADD_SEALS() didn't fail as expected",
> + "fstat() failed",
> + "wrong file size",
> + "open() didn't fail as expected",
> + "open() failed",
> + "mprotect() failed",
> + "fallocate(PUNCH_HOLE) failed",
> + "write() failed",
> + "write() didn't fail as expected",
> + "munmap() failed",
> + "mmap() failed",
> + "mmap() didn't fail as expected",
> + "mmap()+mprotect() didn't fail as expected",
> + "fallocate(PUNCH_HOLE) didn't fail as expected",
> + "ftruncate(SHRINK) didn't fail as expected",
> + "fallocate(ALLOC) failed",
> + "ftruncate(GROW) didn't fail as expected",
> + "fallocate(ALLOC) didn't fail as expected",
> + "pwrite() failed",
> + "pwrite() didn't fail as expected",
> + "read() failed",
> + "read() didn't fail as expected",
> + "close() failed",
> +};
> +
> +#define STRINGIFY(A) #A
> +
> +#define CHECK_RESULT(func2call) ({ \
> + int result; \
> + result = func2call; \
> + if (result < ALL_OK) \
> + tst_brk(TFAIL, "%s: %s", STRINGIFY(func2call), \
> + err_msg[-result]); \
> + else \
> + tst_res(TPASS, "%s", STRINGIFY(func2call)); \
> + result; \
> +})
> +
> +#define ASSERT_HAVE_MEMFD_CREATE() do { \
> + int r; \
> + r = mfd_assert_new("dummy_call", 0, 0); \
> + if (r < 0) \
> + tst_brk(TCONF, "memfd_create does not exist on your system."); \
> + SAFE_CLOSE(r); \
^
The indentation here is confusing.
> + } while (0)
> +
> +static int sys_memfd_create(const char *name,
> + unsigned int flags)
> +{
> + return syscall(__NR_memfd_create, name, flags);
> +}
> +
> +static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
> +{
> + int fd, r;
> +
> + fd = sys_memfd_create(name, flags);
> + if (fd < 0)
> + return -MEMFD_FAIL;
> +
> + r = ftruncate(fd, sz);
> + if (r < 0)
> + return -FTRUNCATE_FAIL;
> +
> + return fd;
> +}
This indirection (returning error number and matching it agains table is
confusing as well).
I wonder if we should do this similarily to safe macros, i.e. doing
something as:
#define SAFE_MFD_NEW(name, sz, flags) \
safe_mfd_new(__FILE__, __LINE__, name, flags);
static int safe_mfd_new(const char *filename, const char lineno,
const char *name, unsigned int flags)
{
int fd;
if (sys_memfd_create(name, flags)) {
tst_brk(TBROK | TERRNO,
"%s:%i: memfd_create(%s,%u) failed",
file, lineno, name, flags);
}
return fd;
}
Then we could do something as:
int fd;
fd = SAFE_MFD_NEW("foo", 0);
SAFE_FTRUNCATE(fd, sz);
In the main test function, which seems to be much clearer code to me.
> +static int mfd_fail_new(const char *name, unsigned int flags)
> +{
> + int fd;
> +
> + fd = sys_memfd_create(name, flags);
> + if (fd >= 0) {
> + close(fd);
> + return -MEMFD_MISMATCH;
> + }
> +
> + return fd;
> +}
> +
> +static int mfd_assert_get_seals(int fd)
> +{
> + int r;
> +
> + r = fcntl(fd, F_GET_SEALS);
> + if (r < 0)
> + return -FCNTL_FAIL;
> +
> + return r;
> +}
This could be just replaced with SAFE_FCNTL() in the test code.
> +static int mfd_assert_has_seals(int fd, int seals)
> +{
> + int s;
> +
> + s = mfd_assert_get_seals(fd);
> + if (s < 0)
> + return s;
> +
> + if (s != seals)
> + return -GET_SEALS_MISMATCH;
> +
> + return ALL_OK;
> +}
This as well, you can even just do:
int s;
s = SAFE_FCNTL(fd, F_GET_SEALS);
if (seals != s) {
tst_res(TFAIL, "Unexpected seals %i, expected %i",
seals, s);
}
> +static int mfd_assert_add_seals(int fd, int seals)
> +{
> + int r;
> +
> + r = mfd_assert_get_seals(fd);
> + if (r < 0)
> + return r;
> +
> + r = fcntl(fd, F_ADD_SEALS, seals);
> + if (r < 0)
> + return -ADD_SEALS_FAIL;
> +
> + return ALL_OK;
> +}
Again this is just one SAFE_FCNTL();
> +static int mfd_fail_add_seals(int fd, int seals)
> +{
> + int r;
> + int s;
> +
> + r = fcntl(fd, F_GET_SEALS);
> + if (r < 0)
> + s = 0;
> + else
> + s = r;
This does not make any sense, is the s variable used for anything at
all?
> + r = fcntl(fd, F_ADD_SEALS, seals);
> + if (r >= 0)
> + return -ADD_SEALS_MISMATCH;
> +
> + return ALL_OK;
> +}
And we can so something as:
#define FAIL_ADD_SEALS(fd, seals) \
fail_add_seals(__FILE__, __LINE__, fd, seals)
static void fail_add_seals(const char *file, const int lineno,
int fd, int seals)
{
if (fcntl(fd, F_ADD_SEALS, seals) >= 0) {
tst_res(TFAIL,
"%s:%i: fcntl(%i, F_ADD_SEALS, %i) passed unexpectedly",
file, lineno, fd, seals);
} else {
tst_res(TPASS, "%s:%i: fcntl(%i, F_ADD_SEALS, %i) failed",
file, lineno, fd, seals);
}
}
For the negative testcases.
> +static int mfd_assert_size(int fd, size_t size)
> +{
> + struct stat st;
> + int r;
> +
> + r = fstat(fd, &st);
> + if (r < 0)
> + return -FSTAT_FAIL;
> + else if (st.st_size != (long)size)
> + return -FILE_SIZE_MISMATCH;
> +
> + return ALL_OK;
> +}
We have SAFE_FSTAT() as well. etc.
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list