From 1c7c3805e33e8294f6f78de60756caf78f00222e Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 24 Aug 2023 05:16:58 +0000 Subject: [PATCH 1/2] 8313319: [linux] mmap should use MAP_FIXED_NOREPLACE if available Backport-of: 3699666c9325b38d287e1d4d2576f8e3a662ae81 --- src/hotspot/os/linux/os_linux.cpp | 30 ++++++++++++++++++++++++-- test/hotspot/gtest/runtime/test_os.cpp | 21 ++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index c6a996d69c3..393f97e9446 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -2885,6 +2885,16 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, STATIC_ASSERT(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value); #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); @@ -3550,8 +3560,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 @@ -4320,6 +4345,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); } diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index fb33cf9c4f9..42b9eaf8450 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -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."); + } +} From 15e6daac1457abc02bd6b8d63e9b70a2a26f5b60 Mon Sep 17 00:00:00 2001 From: Sergey Chernyshev Date: Mon, 17 Nov 2025 11:55:40 +0100 Subject: [PATCH 2/2] fix the double definition that came from 8330520 --- src/hotspot/os/linux/os_linux.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 393f97e9446..daec53fa132 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -2885,16 +2885,6 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, STATIC_ASSERT(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value); #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);