Skip to content

Commit efcf2ff

Browse files
Hardened memory allocator for jme (#2639)
* safer allocator * Update jme3-saferallocator/src/main/java/com/jme3/util/SaferBufferAllocator.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * fixes * Update jme3-saferallocator/src/main/java/com/jme3/util/SaferAllocMemoryGuard.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * fix * fix dep mismatch * make sure saferalloc library is always loaded (fix for SaferAllocNative.* calls that do not check automatically) --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 44ced3d commit efcf2ff

9 files changed

Lines changed: 1005 additions & 3 deletions

File tree

jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTextDialogInput {
7070

7171
private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
72+
private static final String SAFER_BUFFER_ALLOCATOR_CLASS = "com.jme3.util.SaferBufferAllocator";
7273
protected final AtomicBoolean created = new AtomicBoolean(false);
7374
protected final AtomicBoolean renderable = new AtomicBoolean(false);
7475
protected final AtomicBoolean needClose = new AtomicBoolean(false);
@@ -86,7 +87,20 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
8687
final String implementation = BufferAllocatorFactory.PROPERTY_BUFFER_ALLOCATOR_IMPLEMENTATION;
8788

8889
if (System.getProperty(implementation) == null) {
89-
System.setProperty(implementation, PrimitiveAllocator.class.getName());
90+
if (isClassPresent(SAFER_BUFFER_ALLOCATOR_CLASS)) {
91+
System.setProperty(implementation, SAFER_BUFFER_ALLOCATOR_CLASS);
92+
} else {
93+
System.setProperty(implementation, PrimitiveAllocator.class.getName());
94+
}
95+
}
96+
}
97+
98+
private static boolean isClassPresent(String className) {
99+
try {
100+
Class.forName(className, false, OGLESContext.class.getClassLoader());
101+
return true;
102+
} catch (Throwable ignored) {
103+
return false;
90104
}
91105
}
92106

jme3-examples/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies {
2121
implementation project(':jme3-jogg')
2222
// implementation project(':jme3-lwjgl')
2323
implementation project(':jme3-lwjgl3')
24+
implementation project(':jme3-saferallocator')
2425
implementation project(':jme3-networking')
2526
implementation project(':jme3-niftygui')
2627
implementation project(':jme3-plugins')

jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import com.jme3.util.BufferAllocatorFactory;
5959
import com.jme3.util.LWJGLBufferAllocator;
6060
import com.jme3.util.LWJGLBufferAllocator.ConcurrentLWJGLBufferAllocator;
61+
import com.jme3.util.LWJGLSaferAllocMemoryAllocator;
62+
6163
import static com.jme3.util.LWJGLBufferAllocator.PROPERTY_CONCURRENT_BUFFER_ALLOCATOR;
6264
import java.nio.IntBuffer;
6365
import java.util.*;
@@ -79,6 +81,7 @@
7981
import static org.lwjgl.opengl.GL.createCapabilities;
8082
import static org.lwjgl.opengl.GL11.glGetInteger;
8183
import org.lwjgl.opengl.GLCapabilities;
84+
import org.lwjgl.system.Configuration;
8285
import org.lwjgl.system.MemoryStack;
8386
import org.lwjgl.system.Platform;
8487

@@ -90,10 +93,16 @@ public abstract class LwjglContext implements JmeContext {
9093
private static final Logger logger = Logger.getLogger(LwjglContext.class.getName());
9194

9295
static {
93-
9496
final String implementation = BufferAllocatorFactory.PROPERTY_BUFFER_ALLOCATOR_IMPLEMENTATION;
97+
final String configuredImplementation = System.getProperty(implementation);
9598

96-
if (System.getProperty(implementation) == null) {
99+
if (LWJGLSaferAllocMemoryAllocator.isAvailable()) {
100+
Configuration.MEMORY_ALLOCATOR.set(new LWJGLSaferAllocMemoryAllocator());
101+
if (configuredImplementation == null) {
102+
System.setProperty(implementation,
103+
LWJGLSaferAllocMemoryAllocator.SAFER_BUFFER_ALLOCATOR_CLASS);
104+
}
105+
} else if (configuredImplementation == null) {
97106
if (Boolean.parseBoolean(System.getProperty(PROPERTY_CONCURRENT_BUFFER_ALLOCATOR, "true"))) {
98107
System.setProperty(implementation, ConcurrentLWJGLBufferAllocator.class.getName());
99108
} else {
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package com.jme3.util;
2+
3+
import java.lang.invoke.MethodHandle;
4+
import java.lang.invoke.MethodHandles;
5+
import java.lang.invoke.MethodType;
6+
import java.util.logging.Logger;
7+
8+
import org.lwjgl.system.MemoryUtil;
9+
10+
public final class LWJGLSaferAllocMemoryAllocator implements MemoryUtil.MemoryAllocator {
11+
private static final Logger logger = Logger.getLogger(LWJGLSaferAllocMemoryAllocator.class.getName());
12+
13+
public static final String SAFER_BUFFER_ALLOCATOR_CLASS = "com.jme3.util.SaferBufferAllocator";
14+
15+
private static final Bindings BINDINGS = Bindings.create();
16+
17+
private final long fnMalloc;
18+
private final long fnCalloc;
19+
private final long fnRealloc;
20+
private final long fnFree;
21+
private final long fnAlignedAlloc;
22+
private final long fnAlignedFree;
23+
24+
public static boolean isAvailable() {
25+
return BINDINGS != null;
26+
}
27+
28+
public LWJGLSaferAllocMemoryAllocator() {
29+
if (BINDINGS == null) {
30+
throw new IllegalStateException(SAFER_BUFFER_ALLOCATOR_CLASS + " is not available on classpath.");
31+
}
32+
logger.info(getClass().getSimpleName() + " enabled!");
33+
this.fnMalloc = BINDINGS.getMallocFunctionPointer();
34+
this.fnCalloc = BINDINGS.getCallocFunctionPointer();
35+
this.fnRealloc = BINDINGS.getReallocFunctionPointer();
36+
this.fnFree = BINDINGS.getFreeFunctionPointer();
37+
this.fnAlignedAlloc = BINDINGS.getAlignedAllocFunctionPointer();
38+
this.fnAlignedFree = BINDINGS.getAlignedFreeFunctionPointer();
39+
}
40+
41+
@Override
42+
public long getMalloc() {
43+
return fnMalloc;
44+
}
45+
46+
@Override
47+
public long getCalloc() {
48+
return fnCalloc;
49+
}
50+
51+
@Override
52+
public long getRealloc() {
53+
return fnRealloc;
54+
}
55+
56+
@Override
57+
public long getFree() {
58+
return fnFree;
59+
}
60+
61+
@Override
62+
public long getAlignedAlloc() {
63+
return fnAlignedAlloc;
64+
}
65+
66+
@Override
67+
public long getAlignedFree() {
68+
return fnAlignedFree;
69+
}
70+
71+
@Override
72+
public long malloc(long size) {
73+
return BINDINGS.malloc(size);
74+
}
75+
76+
@Override
77+
public long calloc(long num, long size) {
78+
return BINDINGS.calloc(num, size);
79+
}
80+
81+
@Override
82+
public long realloc(long ptr, long size) {
83+
return BINDINGS.realloc(ptr, size);
84+
}
85+
86+
@Override
87+
public void free(long ptr) {
88+
BINDINGS.free(ptr);
89+
}
90+
91+
@Override
92+
public long aligned_alloc(long alignment, long size) {
93+
return BINDINGS.alignedAlloc(alignment, size);
94+
}
95+
96+
@Override
97+
public void aligned_free(long ptr) {
98+
BINDINGS.alignedFree(ptr);
99+
}
100+
101+
private static final class Bindings {
102+
private final MethodHandle mallocFnPtr;
103+
private final MethodHandle callocFnPtr;
104+
private final MethodHandle reallocFnPtr;
105+
private final MethodHandle freeFnPtr;
106+
private final MethodHandle alignedAllocFnPtr;
107+
private final MethodHandle alignedFreeFnPtr;
108+
private final MethodHandle malloc;
109+
private final MethodHandle calloc;
110+
private final MethodHandle realloc;
111+
private final MethodHandle free;
112+
private final MethodHandle alignedAlloc;
113+
private final MethodHandle alignedFree;
114+
115+
private Bindings(
116+
MethodHandle mallocFnPtr,
117+
MethodHandle callocFnPtr,
118+
MethodHandle reallocFnPtr,
119+
MethodHandle freeFnPtr,
120+
MethodHandle alignedAllocFnPtr,
121+
MethodHandle alignedFreeFnPtr,
122+
MethodHandle malloc,
123+
MethodHandle calloc,
124+
MethodHandle realloc,
125+
MethodHandle free,
126+
MethodHandle alignedAlloc,
127+
MethodHandle alignedFree) {
128+
this.mallocFnPtr = mallocFnPtr;
129+
this.callocFnPtr = callocFnPtr;
130+
this.reallocFnPtr = reallocFnPtr;
131+
this.freeFnPtr = freeFnPtr;
132+
this.alignedAllocFnPtr = alignedAllocFnPtr;
133+
this.alignedFreeFnPtr = alignedFreeFnPtr;
134+
this.malloc = malloc;
135+
this.calloc = calloc;
136+
this.realloc = realloc;
137+
this.free = free;
138+
this.alignedAlloc = alignedAlloc;
139+
this.alignedFree = alignedFree;
140+
}
141+
142+
static Bindings create() {
143+
try {
144+
Class<?> clazz = Class.forName(SAFER_BUFFER_ALLOCATOR_CLASS, true,
145+
LWJGLSaferAllocMemoryAllocator.class.getClassLoader());
146+
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
147+
return new Bindings(
148+
lookup.findStatic(clazz, "getMallocFunctionPointer", MethodType.methodType(long.class)),
149+
lookup.findStatic(clazz, "getCallocFunctionPointer", MethodType.methodType(long.class)),
150+
lookup.findStatic(clazz, "getReallocFunctionPointer", MethodType.methodType(long.class)),
151+
lookup.findStatic(clazz, "getFreeFunctionPointer", MethodType.methodType(long.class)),
152+
lookup.findStatic(clazz, "getAlignedAllocFunctionPointer", MethodType.methodType(long.class)),
153+
lookup.findStatic(clazz, "getAlignedFreeFunctionPointer", MethodType.methodType(long.class)),
154+
lookup.findStatic(clazz, "malloc", MethodType.methodType(long.class, long.class)),
155+
lookup.findStatic(clazz, "calloc", MethodType.methodType(long.class, long.class, long.class)),
156+
lookup.findStatic(clazz, "realloc", MethodType.methodType(long.class, long.class, long.class)),
157+
lookup.findStatic(clazz, "free", MethodType.methodType(void.class, long.class)),
158+
lookup.findStatic(clazz, "alignedAlloc",
159+
MethodType.methodType(long.class, long.class, long.class)),
160+
lookup.findStatic(clazz, "alignedFree", MethodType.methodType(void.class, long.class)));
161+
} catch (ReflectiveOperationException | LinkageError e) {
162+
return null;
163+
}
164+
}
165+
166+
long getMallocFunctionPointer() {
167+
try {
168+
return (long) mallocFnPtr.invokeExact();
169+
} catch (Throwable t) {
170+
throw unchecked(t);
171+
}
172+
}
173+
174+
long getCallocFunctionPointer() {
175+
try {
176+
return (long) callocFnPtr.invokeExact();
177+
} catch (Throwable t) {
178+
throw unchecked(t);
179+
}
180+
}
181+
182+
long getReallocFunctionPointer() {
183+
try {
184+
return (long) reallocFnPtr.invokeExact();
185+
} catch (Throwable t) {
186+
throw unchecked(t);
187+
}
188+
}
189+
190+
long getFreeFunctionPointer() {
191+
try {
192+
return (long) freeFnPtr.invokeExact();
193+
} catch (Throwable t) {
194+
throw unchecked(t);
195+
}
196+
}
197+
198+
long getAlignedAllocFunctionPointer() {
199+
try {
200+
return (long) alignedAllocFnPtr.invokeExact();
201+
} catch (Throwable t) {
202+
throw unchecked(t);
203+
}
204+
}
205+
206+
long getAlignedFreeFunctionPointer() {
207+
try {
208+
return (long) alignedFreeFnPtr.invokeExact();
209+
} catch (Throwable t) {
210+
throw unchecked(t);
211+
}
212+
}
213+
214+
long malloc(long size) {
215+
try {
216+
return (long) malloc.invokeExact(size);
217+
} catch (Throwable t) {
218+
throw unchecked(t);
219+
}
220+
}
221+
222+
long calloc(long num, long size) {
223+
try {
224+
return (long) calloc.invokeExact(num, size);
225+
} catch (Throwable t) {
226+
throw unchecked(t);
227+
}
228+
}
229+
230+
long realloc(long ptr, long size) {
231+
try {
232+
return (long) realloc.invokeExact(ptr, size);
233+
} catch (Throwable t) {
234+
throw unchecked(t);
235+
}
236+
}
237+
238+
void free(long ptr) {
239+
try {
240+
free.invokeExact(ptr);
241+
} catch (Throwable t) {
242+
throw unchecked(t);
243+
}
244+
}
245+
246+
long alignedAlloc(long alignment, long size) {
247+
try {
248+
return (long) alignedAlloc.invokeExact(alignment, size);
249+
} catch (Throwable t) {
250+
throw unchecked(t);
251+
}
252+
}
253+
254+
void alignedFree(long ptr) {
255+
try {
256+
alignedFree.invokeExact(ptr);
257+
} catch (Throwable t) {
258+
throw unchecked(t);
259+
}
260+
}
261+
262+
private RuntimeException unchecked(Throwable t) {
263+
if (t instanceof RuntimeException) {
264+
return (RuntimeException) t;
265+
}
266+
if (t instanceof Error) {
267+
throw (Error) t;
268+
}
269+
return new RuntimeException(t);
270+
}
271+
}
272+
}

jme3-saferallocator/build.gradle

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
dependencies {
2+
api project(':jme3-core')
3+
4+
implementation 'org.ngengine:saferalloc:0.0.7'
5+
implementation 'org.ngengine:saferalloc-natives-linux-x86_64:0.0.7'
6+
implementation 'org.ngengine:saferalloc-natives-linux-aarch64:0.0.7'
7+
implementation 'org.ngengine:saferalloc-natives-windows-x86_64:0.0.7'
8+
implementation 'org.ngengine:saferalloc-natives-macos-x86_64:0.0.7'
9+
implementation 'org.ngengine:saferalloc-natives-macos-aarch64:0.0.7'
10+
implementation 'org.ngengine:saferalloc-natives-android:0.0.7'
11+
}
12+
javadoc {
13+
// Disable doclint for JDK8+.
14+
if (JavaVersion.current().isJava8Compatible()){
15+
options.addStringOption('Xdoclint:none', '-quiet')
16+
}
17+
}

0 commit comments

Comments
 (0)