Skip to content

Commit

Permalink
8313319: [linux] mmap should use MAP_FIXED_NOREPLACE if available
Browse files Browse the repository at this point in the history
Reviewed-by: jsjolen, dholmes
  • Loading branch information
tstuefe committed Aug 24, 2023
1 parent 3285a1e commit 3699666
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 2 deletions.
30 changes: 28 additions & 2 deletions src/hotspot/os/linux/os_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2831,6 +2831,16 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec,
#define MADV_HUGEPAGE 14
#endif

// Note that the value for MAP_FIXED_NOREPLACE differs between architectures, but all architectures
// supported by OpenJDK share the same flag value.
#define MAP_FIXED_NOREPLACE_value 0x100000
#ifndef MAP_FIXED_NOREPLACE
#define MAP_FIXED_NOREPLACE MAP_FIXED_NOREPLACE_value
#else
// Sanity-check our assumed default value if we build with a new enough libc.
static_assert(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value);
#endif

int os::Linux::commit_memory_impl(char* addr, size_t size,
size_t alignment_hint, bool exec) {
int err = os::Linux::commit_memory_impl(addr, size, exec);
Expand Down Expand Up @@ -3471,8 +3481,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 @@ -4212,6 +4237,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 @@ -926,3 +926,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.");
}
}

1 comment on commit 3699666

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.