Files
visor/test/syscalls/linux/mremap.cc
T
Jamie LiuandgVisor bot f27cd5f816 Deflake aio_test.
- Most AIO tests call io_setup(nr_events = 128). sizeof(struct io_event)
(128*32 = 4096). However, the actual size of the mapping created by
io_setup() is determined by:

(from fs/aio.c:ioctx_alloc())
/*
 * We keep track of the number of available ringbuffer slots, to prevent
 * overflow (reqs_available), and we also use percpu counters for this.
 *
 * So since up to half the slots might be on other cpu's percpu counters
 * and unavailable, double nr_events so userspace sees what they
 * expected: additionally, we move req_batch slots to/from percpu
 * counters at a time, so make sure that isn't 0:
 */
nr_events = max(nr_events, num_possible_cpus() * 4);
nr_events *= 2;

(from fs/aio.c:aio_setup_ring())
/* Compensate for the ring buffer's head/tail overlap entry */
nr_events += 2; /* 1 is required, 2 for good luck */
size = sizeof(struct aio_ring);
size += sizeof(struct io_event) * nr_events;
nr_pages = PFN_UP(size);

When we mremap() only the first page of a multi-page AIO ring buffer
mapping, fs/aio.c:aio_ring_mremap() updates struct kioctx::mmap_base -
but struct kioctx::mmap_size is untouched, so sys_io_destroy() =>
kill_ioctx() vm_unmaps() the mremapped page, plus some number of pages
after it. Just get the actual size of the mapping from /proc/self/maps.

- Delete test case MremapOver; while it is correct that Linux will not
complain if you overwrite the AIO ring buffer with another mapping, it
won't actually work in the sense that AIO events will not be written to
the new mapping, because Linux stores the struct pages of the ring
buffer in struct kioctx::ring_pages and writes to those through kmap()
rather than using userspace addresses.

- Don't munmap() after mremap(MREMAP_FIXED) returns EFAULT; see new
comment in factored-out test case MremapExpansion.

PiperOrigin-RevId: 267482903
2019-09-05 16:36:44 -07:00

493 lines
18 KiB
C++

// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <string>
#include "gmock/gmock.h"
#include "absl/strings/string_view.h"
#include "test/util/file_descriptor.h"
#include "test/util/logging.h"
#include "test/util/memory_util.h"
#include "test/util/multiprocess_util.h"
#include "test/util/posix_error.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
using ::testing::_;
namespace gvisor {
namespace testing {
namespace {
// Fixture for mremap tests parameterized by mmap flags.
using MremapParamTest = ::testing::TestWithParam<int>;
TEST_P(MremapParamTest, Noop) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, 0, nullptr),
IsPosixErrorOkAndHolds(m.ptr()));
EXPECT_TRUE(IsMapped(m.addr()));
}
TEST_P(MremapParamTest, InPlace_ShrinkingWholeVMA) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// N.B. we must be in a single-threaded subprocess to ensure a
// background thread doesn't concurrently map the second page.
void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr);
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == m.ptr());
MaybeSave();
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(!IsMapped(m.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, InPlace_ShrinkingPartialVMA) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr);
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == m.ptr());
MaybeSave();
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(!IsMapped(m.addr() + kPageSize));
TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, InPlace_ShrinkingAcrossVMAs) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam()));
// Changing permissions on the first page forces it to become a separate vma.
ASSERT_THAT(mprotect(m.ptr(), kPageSize, PROT_NONE), SyscallSucceeds());
const auto rest = [&] {
// Both old_size and new_size now span two vmas; mremap
// shouldn't care.
void* addr = mremap(m.ptr(), 3 * kPageSize, 2 * kPageSize, 0, nullptr);
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == m.ptr());
MaybeSave();
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(IsMapped(m.addr() + kPageSize));
TEST_CHECK(!IsMapped(m.addr() + 2 * kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, InPlace_ExpansionSuccess) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap the second page so that the first can be expanded back into it.
//
// N.B. we must be in a single-threaded subprocess to ensure a
// background thread doesn't concurrently map this page.
TEST_PCHECK(
munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
MaybeSave();
void* addr = mremap(m.ptr(), kPageSize, 2 * kPageSize, 0, nullptr);
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == m.ptr());
MaybeSave();
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(IsMapped(m.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, InPlace_ExpansionFailure) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap the second page, leaving a one-page hole. Trying to expand the
// first page to three pages should fail since the original third page
// is still mapped.
TEST_PCHECK(
munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
MaybeSave();
void* addr = mremap(m.ptr(), kPageSize, 3 * kPageSize, 0, nullptr);
TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded");
TEST_PCHECK_MSG(errno == ENOMEM, "mremap failed with wrong errno");
MaybeSave();
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(!IsMapped(m.addr() + kPageSize));
TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, MayMove_Expansion) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap the second page, leaving a one-page hole. Trying to expand the
// first page to three pages with MREMAP_MAYMOVE should force the
// mapping to be relocated since the original third page is still
// mapped.
TEST_PCHECK(
munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
MaybeSave();
void* addr2 =
mremap(m.ptr(), kPageSize, 3 * kPageSize, MREMAP_MAYMOVE, nullptr);
TEST_PCHECK_MSG(addr2 != MAP_FAILED, "mremap failed");
MaybeSave();
const Mapping m2 = Mapping(addr2, 3 * kPageSize);
TEST_CHECK(m.addr() != m2.addr());
TEST_CHECK(!IsMapped(m.addr()));
TEST_CHECK(!IsMapped(m.addr() + kPageSize));
TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
TEST_CHECK(IsMapped(m2.addr()));
TEST_CHECK(IsMapped(m2.addr() + kPageSize));
TEST_CHECK(IsMapped(m2.addr() + 2 * kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_SourceAndDestinationCannotOverlap) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, m.ptr()),
PosixErrorIs(EINVAL, _));
EXPECT_TRUE(IsMapped(m.addr()));
}
TEST_P(MremapParamTest, Fixed_SameSize) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap dst to create a hole.
TEST_PCHECK(munmap(dst.ptr(), kPageSize) == 0);
MaybeSave();
void* addr = mremap(src.ptr(), kPageSize, kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == dst.ptr());
MaybeSave();
TEST_CHECK(!IsMapped(src.addr()));
TEST_CHECK(IsMapped(dst.addr()));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_SameSize_Unmapping) {
// Like the Fixed_SameSize case, but expect mremap to unmap the destination
// automatically.
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
void* addr = mremap(src.ptr(), kPageSize, kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == dst.ptr());
MaybeSave();
TEST_CHECK(!IsMapped(src.addr()));
TEST_CHECK(IsMapped(dst.addr()));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_ShrinkingWholeVMA) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap dst so we can check that mremap does not keep the
// second page.
TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
MaybeSave();
void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == dst.ptr());
MaybeSave();
TEST_CHECK(!IsMapped(src.addr()));
TEST_CHECK(!IsMapped(src.addr() + kPageSize));
TEST_CHECK(IsMapped(dst.addr()));
TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_ShrinkingPartialVMA) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap dst so we can check that mremap does not keep the
// second page.
TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
MaybeSave();
void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == dst.ptr());
MaybeSave();
TEST_CHECK(!IsMapped(src.addr()));
TEST_CHECK(!IsMapped(src.addr() + kPageSize));
TEST_CHECK(IsMapped(src.addr() + 2 * kPageSize));
TEST_CHECK(IsMapped(dst.addr()));
TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_ShrinkingAcrossVMAs) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam()));
// Changing permissions on the first page forces it to become a separate vma.
ASSERT_THAT(mprotect(src.ptr(), kPageSize, PROT_NONE), SyscallSucceeds());
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unlike flags=0, MREMAP_FIXED requires that [old_address,
// old_address+new_size) only spans a single vma.
void* addr = mremap(src.ptr(), 3 * kPageSize, 2 * kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded");
TEST_PCHECK_MSG(errno == EFAULT, "mremap failed with wrong errno");
MaybeSave();
TEST_CHECK(IsMapped(src.addr()));
TEST_CHECK(IsMapped(src.addr() + kPageSize));
// Despite failing, mremap should have unmapped [old_address+new_size,
// old_address+old_size) (i.e. the third page).
TEST_CHECK(!IsMapped(src.addr() + 2 * kPageSize));
// Despite failing, mremap should have unmapped the destination pages.
TEST_CHECK(!IsMapped(dst.addr()));
TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST_P(MremapParamTest, Fixed_Expansion) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
const auto rest = [&] {
// Unmap dst so we can check that mremap actually maps all pages
// at the destination.
TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
MaybeSave();
void* addr = mremap(src.ptr(), kPageSize, 2 * kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
TEST_CHECK(addr == dst.ptr());
MaybeSave();
TEST_CHECK(!IsMapped(src.addr()));
TEST_CHECK(IsMapped(dst.addr()));
TEST_CHECK(IsMapped(dst.addr() + kPageSize));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
INSTANTIATE_TEST_SUITE_P(PrivateShared, MremapParamTest,
::testing::Values(MAP_PRIVATE, MAP_SHARED));
// mremap with old_size == 0 only works with MAP_SHARED after Linux 4.14
// (dba58d3b8c50 "mm/mremap: fail map duplication attempts for private
// mappings").
TEST(MremapTest, InPlace_Copy) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
EXPECT_THAT(Mremap(m.ptr(), 0, kPageSize, 0, nullptr),
PosixErrorIs(ENOMEM, _));
}
TEST(MremapTest, MayMove_Copy) {
Mapping const m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
// Remainder of this test executes in a subprocess to ensure that if mremap
// incorrectly removes m, it is not remapped by another thread.
const auto rest = [&] {
void* ptr = mremap(m.ptr(), 0, kPageSize, MREMAP_MAYMOVE, nullptr);
MaybeSave();
TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed");
TEST_CHECK(ptr != m.ptr());
TEST_CHECK(IsMapped(m.addr()));
TEST_CHECK(IsMapped(reinterpret_cast<uintptr_t>(ptr)));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST(MremapTest, MustMove_Copy) {
Mapping const src =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
Mapping const dst =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
// Remainder of this test executes in a subprocess to ensure that if mremap
// incorrectly removes src, it is not remapped by another thread.
const auto rest = [&] {
void* ptr = mremap(src.ptr(), 0, kPageSize, MREMAP_MAYMOVE | MREMAP_FIXED,
dst.ptr());
MaybeSave();
TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed");
TEST_CHECK(ptr == dst.ptr());
TEST_CHECK(IsMapped(src.addr()));
TEST_CHECK(IsMapped(dst.addr()));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
void ExpectAllBytesAre(absl::string_view v, char c) {
for (size_t i = 0; i < v.size(); i++) {
ASSERT_EQ(v[i], c) << "at offset " << i;
}
}
TEST(MremapTest, ExpansionPreservesCOWPagesAndExposesNewFilePages) {
// Create a file with 3 pages. The first is filled with 'a', the second is
// filled with 'b', and the third is filled with 'c'.
TempPath const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));
ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'a').c_str(), kPageSize),
SyscallSucceedsWithValue(kPageSize));
ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'b').c_str(), kPageSize),
SyscallSucceedsWithValue(kPageSize));
ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'c').c_str(), kPageSize),
SyscallSucceedsWithValue(kPageSize));
// Create a private mapping of the first 2 pages, and fill the second page
// with 'd'.
Mapping const src = ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, 2 * kPageSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd.get(), 0));
memset(reinterpret_cast<void*>(src.addr() + kPageSize), 'd', kPageSize);
MaybeSave();
// Move the mapping while expanding it to 3 pages. The resulting mapping
// should contain the original first page of the file (filled with 'a'),
// followed by the private copy of the second page (filled with 'd'), followed
// by the newly-mapped third page of the file (filled with 'c').
Mapping const dst = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(3 * kPageSize, PROT_NONE, MAP_PRIVATE));
ASSERT_THAT(Mremap(src.ptr(), 2 * kPageSize, 3 * kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()),
IsPosixErrorOkAndHolds(dst.ptr()));
auto const v = dst.view();
ExpectAllBytesAre(v.substr(0, kPageSize), 'a');
ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'd');
ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), 'c');
}
TEST(MremapDeathTest, SharedAnon) {
SetupGvisorDeathTest();
// Reserve 4 pages of address space.
Mapping const reserved = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(4 * kPageSize, PROT_NONE, MAP_PRIVATE));
// Create a 2-page shared anonymous mapping at the beginning of the
// reservation. Fill the first page with 'a' and the second with 'b'.
Mapping const m = ASSERT_NO_ERRNO_AND_VALUE(
Mmap(reserved.ptr(), 2 * kPageSize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0));
memset(m.ptr(), 'a', kPageSize);
memset(reinterpret_cast<void*>(m.addr() + kPageSize), 'b', kPageSize);
MaybeSave();
// Shrink the mapping to 1 page in-place.
ASSERT_THAT(Mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, m.ptr()),
IsPosixErrorOkAndHolds(m.ptr()));
// Expand the mapping to 3 pages, moving it forward by 1 page in the process
// since the old and new mappings can't overlap.
void* const new_m = reinterpret_cast<void*>(m.addr() + kPageSize);
ASSERT_THAT(Mremap(m.ptr(), kPageSize, 3 * kPageSize,
MREMAP_MAYMOVE | MREMAP_FIXED, new_m),
IsPosixErrorOkAndHolds(new_m));
// The first 2 pages of the mapping should still contain the data we wrote
// (i.e. shrinking should not have discarded the second page's data), while
// touching the third page should raise SIGBUS.
auto const v =
absl::string_view(static_cast<char const*>(new_m), 3 * kPageSize);
ExpectAllBytesAre(v.substr(0, kPageSize), 'a');
ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'b');
EXPECT_EXIT(ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), '\0'),
::testing::KilledBySignal(SIGBUS), "");
}
} // namespace
} // namespace testing
} // namespace gvisor