[LTP] [PATCH v2 5/6] syscalls: added memfd_create dir and memfd_create/memfd_create01.c

Jakub Raček jracek@redhat.com
Tue Mar 7 14:08:24 CET 2017


Hi,

SAFE_MFD_NEW and others should go to "include/tst_safe_macros.h", right?

If I undestand this correctly, negative testcases will have to stay in
memfd_create_common.h. That means I can't use
SAFE_ macros in those functions/macros, as that would obscure line
numbers. And putting those elsewhere would duplicate a lot of code.

Jakub Racek

On 03/07/2017 01:17 PM, Cyril Hrubis wrote:
> 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.
>



More information about the ltp mailing list