blob: 5a37e023834aaff9b6db214cff699aacdadb63fd [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <lib/fit/defer.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "src/starnix/tests/syscalls/cpp/proc_test_base.h"
#include "src/starnix/tests/syscalls/cpp/test_helper.h"
namespace {
class ProcSelfMemProts : public ProcTestBase, public ::testing::WithParamInterface<int> {};
TEST_P(ProcSelfMemProts, CanWriteToPrivateAnonymousMappings) {
if (access("/proc/self/mem", R_OK | W_OK) == -1) {
// Host tests run with read-only /proc, so we can't run this test there.
// See: https://fxbug.dev/328301908
// TODO(https://fxbug.dev/317285180) don't skip on baseline
GTEST_SKIP() << "Cannot write to /proc/self/mem";
}
uint8_t buf[16] = {0};
int prot = GetParam();
const size_t page_size = SAFE_SYSCALL(sysconf(_SC_PAGE_SIZE));
void* mapped = mmap(nullptr, page_size, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(mapped, MAP_FAILED) << "mmap: " << std::strerror(errno);
auto cleanup = fit::defer([mapped, page_size]() { EXPECT_EQ(munmap(mapped, page_size), 0); });
fbl::unique_fd fd = fbl::unique_fd(open("/proc/self/mem", O_RDWR));
ASSERT_TRUE(fd.is_valid()) << "open /proc/self/mem: " << std::strerror(errno);
const off64_t offset = static_cast<off64_t>(reinterpret_cast<uintptr_t>(mapped));
ASSERT_EQ(lseek64(fd.get(), offset, SEEK_SET), offset) << "lseek: " << std::strerror(errno);
memset(buf, 'a', sizeof(buf));
ssize_t n = write(fd.get(), buf, sizeof(buf));
EXPECT_NE(n, -1) << "write: " << std::strerror(errno);
EXPECT_EQ(static_cast<size_t>(n), sizeof(buf));
ASSERT_EQ(mprotect(mapped, page_size, PROT_READ), 0) << "mprotect: " << std::strerror(errno);
EXPECT_EQ(memcmp(mapped, buf, sizeof(buf)), 0);
}
inline std::string ProtToString(const testing::TestParamInfo<int>& info) {
std::string prot = "";
if (info.param == PROT_NONE) {
return "None";
}
if (info.param & PROT_READ) {
prot += "Read";
}
if (info.param & PROT_WRITE) {
prot += "Write";
}
if (info.param & PROT_EXEC) {
prot += "Execute";
}
return prot;
}
INSTANTIATE_TEST_SUITE_P(/* no prefix */, ProcSelfMemProts,
::testing::Values(PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC,
PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC,
PROT_WRITE | PROT_EXEC,
PROT_READ | PROT_WRITE | PROT_EXEC),
ProtToString);
} // namespace