linux/tools/testing/selftests/mm/mdwe_test.c

304 lines
6.3 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
#ifdef __aarch64__
#include <asm/hwcap.h>
#endif
#include <linux/mman.h>
#include <linux/prctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../kselftest_harness.h"
#ifndef __aarch64__
# define PROT_BTI 0
#endif
TEST(prctl_flags)
{
EXPECT_LT(prctl(PR_SET_MDWE, PR_MDWE_NO_INHERIT, 0L, 0L, 7L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
EXPECT_EQ(errno, EINVAL);
EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
EXPECT_EQ(errno, EINVAL);
}
FIXTURE(consecutive_prctl_flags) {};
FIXTURE_SETUP(consecutive_prctl_flags) {}
FIXTURE_TEARDOWN(consecutive_prctl_flags) {}
FIXTURE_VARIANT(consecutive_prctl_flags)
{
unsigned long first_flags;
unsigned long second_flags;
bool should_work;
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags)
{
.first_flags = 0,
.second_flags = 0,
.should_work = true,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_exec_gain)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
.second_flags = PR_MDWE_REFUSE_EXEC_GAIN,
.should_work = true,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_both_flags)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
.second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
.should_work = true,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
.second_flags = 0,
.should_work = false,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe_no_inherit)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
.second_flags = 0,
.should_work = false,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_no_inherit)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
.second_flags = PR_MDWE_REFUSE_EXEC_GAIN,
.should_work = false,
};
FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_enable_no_inherit)
{
.first_flags = PR_MDWE_REFUSE_EXEC_GAIN,
.second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT,
.should_work = false,
};
TEST_F(consecutive_prctl_flags, two_prctls)
{
int ret;
EXPECT_EQ(prctl(PR_SET_MDWE, variant->first_flags, 0L, 0L, 0L), 0);
ret = prctl(PR_SET_MDWE, variant->second_flags, 0L, 0L, 0L);
if (variant->should_work) {
EXPECT_EQ(ret, 0);
ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
ASSERT_EQ(ret, variant->second_flags);
} else {
EXPECT_NE(ret, 0);
ASSERT_EQ(errno, EPERM);
}
}
FIXTURE(mdwe)
{
void *p;
int flags;
size_t size;
pid_t pid;
};
FIXTURE_VARIANT(mdwe)
{
bool enabled;
bool forked;
bool inherit;
};
FIXTURE_VARIANT_ADD(mdwe, stock)
{
kselftest: vm: fix tabs/spaces inconsistency in the mdwe test Patch series "MDWE without inheritance", v4. Joey recently introduced a Memory-Deny-Write-Executable (MDWE) prctl which tags current with a flag that prevents pages that were previously not executable from becoming executable. This tag always gets inherited by children tasks. (it's in MMF_INIT_MASK) At Google, we've been using a somewhat similar downstream patch for a few years now. To make the adoption of this feature easier, we've had it support a mode in which the W^X flag does not propagate to children. For example, this is handy if a C process which wants W^X protection suspects it could start children processes that would use a JIT. I'd like to align our features with the upstream prctl. This series proposes a new NO_INHERIT flag to the MDWE prctl to make this kind of adoption easier. It sets a different flag in current that is not in MMF_INIT_MASK and which does not propagate. As part of looking into MDWE, I also fixed a couple of things in the MDWE test. The background for this was discussed in these threads: v1: https://lore.kernel.org/all/66900d0ad42797a55259061f757beece@ispras.ru/ v2: https://lore.kernel.org/all/d7e3749c-a718-df94-92af-1cb0fecab772@redhat.com/ This patch (of 6): Fix tabs/spaces inconsistency in the mdwe test. Link: https://lkml.kernel.org/r/20230828150858.393570-1-revest@chromium.org Link: https://lkml.kernel.org/r/20230828150858.393570-2-revest@chromium.org Signed-off-by: Florent Revest <revest@chromium.org> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Ayush Jain <ayush.jain3@amd.com> Cc: Greg Thelen <gthelen@google.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: KP Singh <kpsingh@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Peter Xu <peterx@redhat.com> Cc: Szabolcs Nagy <Szabolcs.Nagy@arm.com> Cc: Topi Miettinen <toiwoton@gmail.com> Cc: Ryan Roberts <ryan.roberts@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-28 17:08:53 +02:00
.enabled = false,
.forked = false,
.inherit = false,
};
FIXTURE_VARIANT_ADD(mdwe, enabled)
{
kselftest: vm: fix tabs/spaces inconsistency in the mdwe test Patch series "MDWE without inheritance", v4. Joey recently introduced a Memory-Deny-Write-Executable (MDWE) prctl which tags current with a flag that prevents pages that were previously not executable from becoming executable. This tag always gets inherited by children tasks. (it's in MMF_INIT_MASK) At Google, we've been using a somewhat similar downstream patch for a few years now. To make the adoption of this feature easier, we've had it support a mode in which the W^X flag does not propagate to children. For example, this is handy if a C process which wants W^X protection suspects it could start children processes that would use a JIT. I'd like to align our features with the upstream prctl. This series proposes a new NO_INHERIT flag to the MDWE prctl to make this kind of adoption easier. It sets a different flag in current that is not in MMF_INIT_MASK and which does not propagate. As part of looking into MDWE, I also fixed a couple of things in the MDWE test. The background for this was discussed in these threads: v1: https://lore.kernel.org/all/66900d0ad42797a55259061f757beece@ispras.ru/ v2: https://lore.kernel.org/all/d7e3749c-a718-df94-92af-1cb0fecab772@redhat.com/ This patch (of 6): Fix tabs/spaces inconsistency in the mdwe test. Link: https://lkml.kernel.org/r/20230828150858.393570-1-revest@chromium.org Link: https://lkml.kernel.org/r/20230828150858.393570-2-revest@chromium.org Signed-off-by: Florent Revest <revest@chromium.org> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Ayush Jain <ayush.jain3@amd.com> Cc: Greg Thelen <gthelen@google.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: KP Singh <kpsingh@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Peter Xu <peterx@redhat.com> Cc: Szabolcs Nagy <Szabolcs.Nagy@arm.com> Cc: Topi Miettinen <toiwoton@gmail.com> Cc: Ryan Roberts <ryan.roberts@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-28 17:08:53 +02:00
.enabled = true,
.forked = false,
.inherit = true,
};
FIXTURE_VARIANT_ADD(mdwe, inherited)
{
kselftest: vm: fix tabs/spaces inconsistency in the mdwe test Patch series "MDWE without inheritance", v4. Joey recently introduced a Memory-Deny-Write-Executable (MDWE) prctl which tags current with a flag that prevents pages that were previously not executable from becoming executable. This tag always gets inherited by children tasks. (it's in MMF_INIT_MASK) At Google, we've been using a somewhat similar downstream patch for a few years now. To make the adoption of this feature easier, we've had it support a mode in which the W^X flag does not propagate to children. For example, this is handy if a C process which wants W^X protection suspects it could start children processes that would use a JIT. I'd like to align our features with the upstream prctl. This series proposes a new NO_INHERIT flag to the MDWE prctl to make this kind of adoption easier. It sets a different flag in current that is not in MMF_INIT_MASK and which does not propagate. As part of looking into MDWE, I also fixed a couple of things in the MDWE test. The background for this was discussed in these threads: v1: https://lore.kernel.org/all/66900d0ad42797a55259061f757beece@ispras.ru/ v2: https://lore.kernel.org/all/d7e3749c-a718-df94-92af-1cb0fecab772@redhat.com/ This patch (of 6): Fix tabs/spaces inconsistency in the mdwe test. Link: https://lkml.kernel.org/r/20230828150858.393570-1-revest@chromium.org Link: https://lkml.kernel.org/r/20230828150858.393570-2-revest@chromium.org Signed-off-by: Florent Revest <revest@chromium.org> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Ayush Jain <ayush.jain3@amd.com> Cc: Greg Thelen <gthelen@google.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: KP Singh <kpsingh@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Peter Xu <peterx@redhat.com> Cc: Szabolcs Nagy <Szabolcs.Nagy@arm.com> Cc: Topi Miettinen <toiwoton@gmail.com> Cc: Ryan Roberts <ryan.roberts@arm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-28 17:08:53 +02:00
.enabled = true,
.forked = true,
.inherit = true,
};
FIXTURE_VARIANT_ADD(mdwe, not_inherited)
{
.enabled = true,
.forked = true,
.inherit = false,
};
static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant)
{
return variant->enabled && (!variant->forked || variant->inherit);
}
FIXTURE_SETUP(mdwe)
{
unsigned long mdwe_flags;
int ret, status;
self->p = NULL;
self->flags = MAP_SHARED | MAP_ANONYMOUS;
self->size = getpagesize();
if (!variant->enabled)
return;
mdwe_flags = PR_MDWE_REFUSE_EXEC_GAIN;
if (!variant->inherit)
mdwe_flags |= PR_MDWE_NO_INHERIT;
ret = prctl(PR_SET_MDWE, mdwe_flags, 0L, 0L, 0L);
ASSERT_EQ(ret, 0) {
TH_LOG("PR_SET_MDWE failed or unsupported");
}
ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
ASSERT_EQ(ret, mdwe_flags);
if (variant->forked) {
self->pid = fork();
ASSERT_GE(self->pid, 0) {
TH_LOG("fork failed\n");
}
if (self->pid > 0) {
ret = waitpid(self->pid, &status, 0);
ASSERT_TRUE(WIFEXITED(status));
exit(WEXITSTATUS(status));
}
}
}
FIXTURE_TEARDOWN(mdwe)
{
if (self->p && self->p != MAP_FAILED)
munmap(self->p, self->size);
}
TEST_F(mdwe, mmap_READ_EXEC)
{
self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
EXPECT_NE(self->p, MAP_FAILED);
}
TEST_F(mdwe, mmap_WRITE_EXEC)
{
self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
if (executable_map_should_fail(variant)) {
EXPECT_EQ(self->p, MAP_FAILED);
} else {
EXPECT_NE(self->p, MAP_FAILED);
}
}
TEST_F(mdwe, mprotect_stay_EXEC)
{
int ret;
self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
ASSERT_NE(self->p, MAP_FAILED);
ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
EXPECT_EQ(ret, 0);
}
TEST_F(mdwe, mprotect_add_EXEC)
{
int ret;
self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
ASSERT_NE(self->p, MAP_FAILED);
ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
if (executable_map_should_fail(variant)) {
EXPECT_LT(ret, 0);
} else {
EXPECT_EQ(ret, 0);
}
}
TEST_F(mdwe, mprotect_WRITE_EXEC)
{
int ret;
self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
ASSERT_NE(self->p, MAP_FAILED);
ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
if (executable_map_should_fail(variant)) {
EXPECT_LT(ret, 0);
} else {
EXPECT_EQ(ret, 0);
}
}
TEST_F(mdwe, mmap_FIXED)
{
void *p;
self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
ASSERT_NE(self->p, MAP_FAILED);
kselftest: vm: fix mdwe's mmap_FIXED test case I checked with the original author, the mmap_FIXED test case wasn't properly tested and fails. Currently, it maps two consecutive (non overlapping) pages and expects the second mapping to be denied by MDWE but these two pages have nothing to do with each other so MDWE is actually out of the picture here. What the test actually intended to do was to remap a virtual address using MAP_FIXED. However, this operation unmaps the existing mapping and creates a new one so the va is backed by a new page and MDWE is again out of the picture, all remappings should succeed. This patch keeps the test case to make it clear that this situation is expected to work: MDWE shouldn't block a MAP_FIXED replacement. Link: https://lkml.kernel.org/r/20230828150858.393570-3-revest@chromium.org Fixes: 4cf1fe34fd18 ("kselftest: vm: add tests for memory-deny-write-execute") Signed-off-by: Florent Revest <revest@chromium.org> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Ryan Roberts <ryan.roberts@arm.com> Tested-by: Ryan Roberts <ryan.roberts@arm.com> Tested-by: Ayush Jain <ayush.jain3@amd.com> Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Greg Thelen <gthelen@google.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: KP Singh <kpsingh@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Peter Xu <peterx@redhat.com> Cc: Szabolcs Nagy <Szabolcs.Nagy@arm.com> Cc: Topi Miettinen <toiwoton@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-28 17:08:54 +02:00
/* MAP_FIXED unmaps the existing page before mapping which is allowed */
p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
self->flags | MAP_FIXED, 0, 0);
kselftest: vm: fix mdwe's mmap_FIXED test case I checked with the original author, the mmap_FIXED test case wasn't properly tested and fails. Currently, it maps two consecutive (non overlapping) pages and expects the second mapping to be denied by MDWE but these two pages have nothing to do with each other so MDWE is actually out of the picture here. What the test actually intended to do was to remap a virtual address using MAP_FIXED. However, this operation unmaps the existing mapping and creates a new one so the va is backed by a new page and MDWE is again out of the picture, all remappings should succeed. This patch keeps the test case to make it clear that this situation is expected to work: MDWE shouldn't block a MAP_FIXED replacement. Link: https://lkml.kernel.org/r/20230828150858.393570-3-revest@chromium.org Fixes: 4cf1fe34fd18 ("kselftest: vm: add tests for memory-deny-write-execute") Signed-off-by: Florent Revest <revest@chromium.org> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Ryan Roberts <ryan.roberts@arm.com> Tested-by: Ryan Roberts <ryan.roberts@arm.com> Tested-by: Ayush Jain <ayush.jain3@amd.com> Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Greg Thelen <gthelen@google.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: KP Singh <kpsingh@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Peter Xu <peterx@redhat.com> Cc: Szabolcs Nagy <Szabolcs.Nagy@arm.com> Cc: Topi Miettinen <toiwoton@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-28 17:08:54 +02:00
EXPECT_EQ(p, self->p);
}
TEST_F(mdwe, arm64_BTI)
{
int ret;
#ifdef __aarch64__
if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
#endif
SKIP(return, "HWCAP2_BTI not supported");
self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
ASSERT_NE(self->p, MAP_FAILED);
ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
EXPECT_EQ(ret, 0);
}
TEST_HARNESS_MAIN