Skip to content

Commit 3699666

Browse files
committedAug 24, 2023
8313319: [linux] mmap should use MAP_FIXED_NOREPLACE if available
Reviewed-by: jsjolen, dholmes
1 parent 3285a1e commit 3699666

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed
 

‎src/hotspot/os/linux/os_linux.cpp

+28-2
Original file line numberDiff line numberDiff line change
@@ -2831,6 +2831,16 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec,
28312831
#define MADV_HUGEPAGE 14
28322832
#endif
28332833

2834+
// Note that the value for MAP_FIXED_NOREPLACE differs between architectures, but all architectures
2835+
// supported by OpenJDK share the same flag value.
2836+
#define MAP_FIXED_NOREPLACE_value 0x100000
2837+
#ifndef MAP_FIXED_NOREPLACE
2838+
#define MAP_FIXED_NOREPLACE MAP_FIXED_NOREPLACE_value
2839+
#else
2840+
// Sanity-check our assumed default value if we build with a new enough libc.
2841+
static_assert(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value);
2842+
#endif
2843+
28342844
int os::Linux::commit_memory_impl(char* addr, size_t size,
28352845
size_t alignment_hint, bool exec) {
28362846
int err = os::Linux::commit_memory_impl(addr, size, exec);
@@ -3471,8 +3481,23 @@ bool os::remove_stack_guard_pages(char* addr, size_t size) {
34713481
// may not start from the requested address. Unlike Linux mmap(), this
34723482
// function returns null to indicate failure.
34733483
static char* anon_mmap(char* requested_addr, size_t bytes) {
3474-
// MAP_FIXED is intentionally left out, to leave existing mappings intact.
3475-
const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS;
3484+
// If a requested address was given:
3485+
//
3486+
// The POSIX-conforming way is to *omit* MAP_FIXED. This will leave existing mappings intact.
3487+
// If the requested mapping area is blocked by a pre-existing mapping, the kernel will map
3488+
// somewhere else. On Linux, that alternative address appears to have no relation to the
3489+
// requested address.
3490+
// Unfortunately, this is not what we need - if we requested a specific address, we'd want
3491+
// to map there and nowhere else. Therefore we will unmap the block again, which means we
3492+
// just executed a needless mmap->munmap cycle.
3493+
// Since Linux 4.17, the kernel offers MAP_FIXED_NOREPLACE. With this flag, if a pre-
3494+
// existing mapping exists, the kernel will not map at an alternative point but instead
3495+
// return an error. We can therefore save that unnecessary mmap-munmap cycle.
3496+
//
3497+
// Backward compatibility: Older kernels will ignore the unknown flag; so mmap will behave
3498+
// as in mode (a).
3499+
const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS |
3500+
((requested_addr != nullptr) ? MAP_FIXED_NOREPLACE : 0);
34763501

34773502
// Map reserved/uncommitted pages PROT_NONE so we fail early if we
34783503
// touch an uncommitted page. Otherwise, the read/write might
@@ -4212,6 +4237,7 @@ char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, bool
42124237

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

‎test/hotspot/gtest/runtime/test_os.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -926,3 +926,24 @@ TEST_VM(os, open_O_CLOEXEC) {
926926
::close(fd);
927927
#endif
928928
}
929+
930+
TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_smallpages) {
931+
char* p1 = os::reserve_memory(M, false, mtTest);
932+
ASSERT_NE(p1, nullptr);
933+
char* p2 = os::attempt_reserve_memory_at(p1, M);
934+
ASSERT_EQ(p2, nullptr); // should have failed
935+
os::release_memory(p1, M);
936+
}
937+
938+
TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_largepages) {
939+
if (UseLargePages && !os::can_commit_large_page_memory()) { // aka special
940+
const size_t lpsz = os::large_page_size();
941+
char* p1 = os::reserve_memory_aligned(lpsz, lpsz, false);
942+
ASSERT_NE(p1, nullptr);
943+
char* p2 = os::reserve_memory_special(lpsz, lpsz, lpsz, p1, false);
944+
ASSERT_EQ(p2, nullptr); // should have failed
945+
os::release_memory(p1, M);
946+
} else {
947+
tty->print_cr("Skipped.");
948+
}
949+
}

0 commit comments

Comments
 (0)
Please sign in to comment.