Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/hotspot/os/linux/os_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3550,8 +3550,23 @@ bool os::remove_stack_guard_pages(char* addr, size_t size) {
// may not start from the requested address. Unlike Linux mmap(), this
// function returns null to indicate failure.
static char* anon_mmap(char* requested_addr, size_t bytes) {
// MAP_FIXED is intentionally left out, to leave existing mappings intact.
const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS;
// If a requested address was given:
//
// The POSIX-conforming way is to *omit* MAP_FIXED. This will leave existing mappings intact.
// If the requested mapping area is blocked by a pre-existing mapping, the kernel will map
// somewhere else. On Linux, that alternative address appears to have no relation to the
// requested address.
// Unfortunately, this is not what we need - if we requested a specific address, we'd want
// to map there and nowhere else. Therefore we will unmap the block again, which means we
// just executed a needless mmap->munmap cycle.
// Since Linux 4.17, the kernel offers MAP_FIXED_NOREPLACE. With this flag, if a pre-
// existing mapping exists, the kernel will not map at an alternative point but instead
// return an error. We can therefore save that unnecessary mmap-munmap cycle.
//
// Backward compatibility: Older kernels will ignore the unknown flag; so mmap will behave
// as in mode (a).
const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS |
((requested_addr != nullptr) ? MAP_FIXED_NOREPLACE : 0);

// Map reserved/uncommitted pages PROT_NONE so we fail early if we
// touch an uncommitted page. Otherwise, the read/write might
Expand Down Expand Up @@ -4320,6 +4335,7 @@ char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, bool

if (addr != nullptr) {
// mmap() is successful but it fails to reserve at the requested address
log_trace(os, map)("Kernel rejected " PTR_FORMAT ", offered " PTR_FORMAT ".", p2i(requested_addr), p2i(addr));
anon_munmap(addr, bytes);
}

Expand Down
21 changes: 21 additions & 0 deletions test/hotspot/gtest/runtime/test_os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,3 +931,24 @@ TEST_VM(os, open_O_CLOEXEC) {
::close(fd);
#endif
}

TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_smallpages) {
char* p1 = os::reserve_memory(M, false, mtTest);
ASSERT_NE(p1, nullptr);
char* p2 = os::attempt_reserve_memory_at(p1, M);
ASSERT_EQ(p2, nullptr); // should have failed
os::release_memory(p1, M);
}

TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_largepages) {
if (UseLargePages && !os::can_commit_large_page_memory()) { // aka special
const size_t lpsz = os::large_page_size();
char* p1 = os::reserve_memory_aligned(lpsz, lpsz, false);
ASSERT_NE(p1, nullptr);
char* p2 = os::reserve_memory_special(lpsz, lpsz, lpsz, p1, false);
ASSERT_EQ(p2, nullptr); // should have failed
os::release_memory(p1, M);
} else {
tty->print_cr("Skipped.");
}
}