From 0d1bb66558b84799ab022c95743d3361a61fa7e2 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Tue, 11 Nov 2025 11:52:44 +0100 Subject: [PATCH 1/5] 8371637: allocateNativeInternal sometimes return incorrectly aligned memory jdk.internal.foreign.SegmentFactories::allocateNativeInternal assumes that the underlying implementation of malloc aligns allocations on 16 byte boundaries for 64 bit platforms, and 8 byte boundaries on 32 bit platforms. So for any allocation where the requested alignment is less than or equal to this default alignment it makes no adjustment. However, this assumption does not hold for all allocators. Specifically jemallc, used by libc on FreeBSD will align small allocations on 8 or 4 byte boundaries, respectively. This causes allocateNativeInternal to sometimes return memory that is not properly aligned when the requested alignment is exactly 16 bytes. To make sure we honour the requested alignment when it exaclty matches the quantum as defined by MAX_MALLOC_ALIGN, this patch ensures that we adjust the alignment also in this case. This should make no difference for platforms where malloc allready aligns on the quantum, except for a few unnecessary trivial calculations. This work was sponsored by: The FreeBSD Foundation --- .../share/classes/jdk/internal/foreign/SegmentFactories.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java index 3edcac2e44c92..101051060ead0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -203,7 +203,7 @@ private static long allocateNativeInternal(long byteSize, long byteAlignment, Me long allocationSize; long allocationBase; long result; - if (byteAlignment > MAX_MALLOC_ALIGN) { + if (byteAlignment >= MAX_MALLOC_ALIGN) { allocationSize = alignedSize + byteAlignment - MAX_MALLOC_ALIGN; if (shouldReserve) { AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(allocationSize, byteSize); From 2090700643dddddbde3bef1893659a1f687b26f5 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Thu, 13 Nov 2025 11:28:57 +0100 Subject: [PATCH 2/5] Test that native segments don't overlap This work was sponsored by: The FreeBSD Foundation --- test/jdk/java/foreign/TestMemoryAlignment.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index 44d28a07b05e3..a73635c8f08df 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -60,8 +60,14 @@ public void testAlignedAccess(long align) { assertEquals(aligned.byteAlignment(), align); //unreasonable alignment here, to make sure access throws VarHandle vh = aligned.varHandle(); try (Arena arena = Arena.ofConfined()) { - MemorySegment segment = arena.allocate(aligned);; + MemorySegment segment = arena.allocate(aligned); vh.set(segment, 0L, -42); + + // Allocate another segment and fill it with data to + // check that the first segment is not overwritten + MemorySegment nextSegment = arena.allocate(aligned); + vh.set(nextSegment, 0L, 0xffffff); + int val = (int)vh.get(segment, 0L); assertEquals(val, -42); } From 2b8266f682d96212baab6e17c72eba335b77e130 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Thu, 13 Nov 2025 11:50:21 +0100 Subject: [PATCH 3/5] Second try to fix alignment for native segments Introducing a helper function as suggested by JornVernee to decide on the proper alignment based on the segment size. This work was sponsored by: The FreeBSD Foundation Co-authored-by: JornVernee --- .../internal/foreign/SegmentFactories.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java index 101051060ead0..55c7dffa2ead4 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -48,10 +48,6 @@ */ public class SegmentFactories { - // The maximum alignment supported by malloc - typically 16 bytes on - // 64-bit platforms and 8 bytes on 32-bit platforms. - private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); // Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks @@ -203,8 +199,9 @@ private static long allocateNativeInternal(long byteSize, long byteAlignment, Me long allocationSize; long allocationBase; long result; - if (byteAlignment >= MAX_MALLOC_ALIGN) { - allocationSize = alignedSize + byteAlignment - MAX_MALLOC_ALIGN; + long defaultAlignment = alignmentForSize(alignedSize); + if (byteAlignment > defaultAlignment) { + allocationSize = alignedSize + byteAlignment - defaultAlignment; if (shouldReserve) { AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(allocationSize, byteSize); } @@ -250,6 +247,16 @@ private static long allocateMemoryWrapper(long size) { } } + private static final boolean IS_FREEBSD = System.getProperty("os.name").equals("FreeBSD"); + + private static long alignmentForSize(long size) { + if (IS_FREEBSD && size <= Double.BYTES) { + return Double.BYTES; + } else { + return Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; + } + } + public static MappedMemorySegmentImpl mapSegment(long size, UnmapperProxy unmapper, boolean readOnly, MemorySessionImpl sessionImpl) { ensureInitialized(); if (unmapper != null) { From 15daa51b06aaf4cda7b85e5326d8652f838d1a79 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Fri, 14 Nov 2025 13:46:24 +0100 Subject: [PATCH 4/5] OS agnostic fix for alignment of native segments Only align up the requested memory if the requested alignment is larget than max alignment provided by malloc, or if the requested size is not a multiple of the alignment size. This work was sponsored by: The FreeBSD Foundation Co-authored-by: mcimadamore --- .../internal/foreign/SegmentFactories.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java index 55c7dffa2ead4..3b62c6eabf6e0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -48,6 +48,10 @@ */ public class SegmentFactories { + // The maximum alignment supported by malloc - typically 16 bytes on + // 64-bit platforms and 8 bytes on 32-bit platforms. + private static final long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); // Unsafe native segment factories. These are used by the implementation code, to skip the sanity checks @@ -199,9 +203,8 @@ private static long allocateNativeInternal(long byteSize, long byteAlignment, Me long allocationSize; long allocationBase; long result; - long defaultAlignment = alignmentForSize(alignedSize); - if (byteAlignment > defaultAlignment) { - allocationSize = alignedSize + byteAlignment - defaultAlignment; + if (byteAlignment > MAX_MALLOC_ALIGN || alignedSize % byteAlignment != 0) { + allocationSize = alignedSize + byteAlignment - MAX_MALLOC_ALIGN; if (shouldReserve) { AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(allocationSize, byteSize); } @@ -247,16 +250,6 @@ private static long allocateMemoryWrapper(long size) { } } - private static final boolean IS_FREEBSD = System.getProperty("os.name").equals("FreeBSD"); - - private static long alignmentForSize(long size) { - if (IS_FREEBSD && size <= Double.BYTES) { - return Double.BYTES; - } else { - return Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; - } - } - public static MappedMemorySegmentImpl mapSegment(long size, UnmapperProxy unmapper, boolean readOnly, MemorySessionImpl sessionImpl) { ensureInitialized(); if (unmapper != null) { From c723058e75dfc519f0b8a69b53a84cbe571e83d5 Mon Sep 17 00:00:00 2001 From: Harald Eilertsen Date: Sun, 16 Nov 2025 18:56:57 +0100 Subject: [PATCH 5/5] Fix calculation of allocationSize when byteAlignment < MAX_MALLOC_ALIGN This work was sponsored by: The FreeBSD Foundation --- .../share/classes/jdk/internal/foreign/SegmentFactories.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java index 3b62c6eabf6e0..f898ded7d9133 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SegmentFactories.java @@ -204,7 +204,7 @@ private static long allocateNativeInternal(long byteSize, long byteAlignment, Me long allocationBase; long result; if (byteAlignment > MAX_MALLOC_ALIGN || alignedSize % byteAlignment != 0) { - allocationSize = alignedSize + byteAlignment - MAX_MALLOC_ALIGN; + allocationSize = alignedSize + byteAlignment - Math.min(MAX_MALLOC_ALIGN, alignedSize); if (shouldReserve) { AbstractMemorySegmentImpl.NIO_ACCESS.reserveMemory(allocationSize, byteSize); }