blob: 04a5f4f90fbcad0112d8f618391a672b79812e34 [file] [log] [blame]
/*
* 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,
};