Add README and SecureBuffer memory locking
This commit is contained in:
parent
bc1a7a8952
commit
7c6642085f
7 changed files with 829 additions and 143 deletions
|
|
@ -3,8 +3,10 @@ package ai.nomyo;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
|
@ -21,12 +23,49 @@ public final class SecureMemory {
|
|||
@Setter
|
||||
private static volatile boolean secureMemoryEnabled = true;
|
||||
|
||||
private static final MethodHandle MLOCK_HANDLE;
|
||||
private static final MethodHandle MUNLOCK_HANDLE;
|
||||
|
||||
static {
|
||||
boolean locking = false;
|
||||
boolean zeroing = false;
|
||||
MethodHandle mlockHandle = null;
|
||||
MethodHandle munlockHandle = null;
|
||||
|
||||
try {
|
||||
locking = initMemoryLocking();
|
||||
Linker linker = Linker.nativeLinker();
|
||||
SymbolLookup stdLib = linker.defaultLookup();
|
||||
|
||||
var mlockOpt = stdLib.find("mlock");
|
||||
var munlockOpt = stdLib.find("munlock");
|
||||
|
||||
if (mlockOpt.isPresent() && munlockOpt.isPresent()) {
|
||||
MemorySegment mlockAddr = mlockOpt.get();
|
||||
MemorySegment munlockAddr = munlockOpt.get();
|
||||
FunctionDescriptor mlockDesc = FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_LONG);
|
||||
|
||||
FunctionDescriptor munlockDesc = FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_LONG);
|
||||
|
||||
mlockHandle = linker.downcallHandle(mlockAddr, mlockDesc);
|
||||
munlockHandle = linker.downcallHandle(munlockAddr, munlockDesc);
|
||||
|
||||
locking = true;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Degrade gracefully
|
||||
}
|
||||
|
||||
MLOCK_HANDLE = mlockHandle;
|
||||
MUNLOCK_HANDLE = munlockHandle;
|
||||
|
||||
try {
|
||||
locking = initMemoryLocking(locking);
|
||||
zeroing = true; // Secure zeroing is always available at the JVM level
|
||||
} catch (Throwable t) {
|
||||
// Degrade gracefully
|
||||
|
|
@ -36,9 +75,18 @@ public final class SecureMemory {
|
|||
HAS_SECURE_ZEROING = zeroing;
|
||||
}
|
||||
|
||||
private static boolean initMemoryLocking() {
|
||||
// FFM doesn't support memory locking at this point in time
|
||||
return false;
|
||||
private static boolean initMemoryLocking(boolean preCheck) {
|
||||
if (MLOCK_HANDLE == null || MUNLOCK_HANDLE == null || !preCheck) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment testSegment = arena.allocate(1);
|
||||
long result = (long) MLOCK_HANDLE.invokeExact(testSegment, (long) 1);
|
||||
return result == 0;
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +141,7 @@ public final class SecureMemory {
|
|||
private boolean closed;
|
||||
|
||||
/**
|
||||
* @param data byte array to wrap
|
||||
* @param data byte array to wrap (zeroed after copy to off-heap memory)
|
||||
* @param lock whether to attempt memory locking
|
||||
*/
|
||||
public SecureBuffer(byte[] data, boolean lock) {
|
||||
|
|
@ -102,6 +150,7 @@ public final class SecureMemory {
|
|||
|
||||
if (data != null) {
|
||||
this.data.asByteBuffer().put(data);
|
||||
Arrays.fill(data, (byte) 0);
|
||||
}
|
||||
|
||||
this.size = this.data.byteSize();
|
||||
|
|
@ -119,16 +168,40 @@ public final class SecureMemory {
|
|||
* Locks buffer in memory (prevents disk swapping). Returns false if unavailable.
|
||||
*/
|
||||
public boolean lock() {
|
||||
return false;
|
||||
if (this.locked || this.address == 0) {
|
||||
return this.locked;
|
||||
}
|
||||
|
||||
try {
|
||||
long result = (long) MLOCK_HANDLE.invokeExact(
|
||||
MemorySegment.ofAddress(this.address),
|
||||
this.size);
|
||||
this.locked = result == 0;
|
||||
} catch (Throwable t) {
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
return this.locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks buffer (allows disk swapping).
|
||||
*/
|
||||
public boolean unlock() {
|
||||
if (!locked) return false;
|
||||
locked = false;
|
||||
return false;
|
||||
if (!locked || this.address == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
long result = (long) MUNLOCK_HANDLE.invokeExact(
|
||||
MemorySegment.ofAddress(this.address),
|
||||
this.size);
|
||||
locked = result != 0;
|
||||
return result == 0;
|
||||
} catch (Throwable t) {
|
||||
locked = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue