Skip to content

Commit b7da2ca

Browse files
committed
Fix memory.grow out-of-memory case.
If reallocating the memory fails, `memory.grow` should not throw but return -1 and keep the current buffer. Previously, in the case of ByteArrayWasmMemory, the buffer would be set to null which could then cause a NullPointerException.
1 parent 3f8a5b5 commit b7da2ca

File tree

4 files changed

+52
-38
lines changed

4 files changed

+52
-38
lines changed

wasm/mx.wasm/truffle.tck.permissions/unsafe_excludes.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@
281281
"name" : "allocate",
282282
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
283283
}, {
284-
"name" : "grow",
284+
"name" : "reallocate",
285285
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
286286
}, {
287287
"name" : "load_i32",

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@
5050
import java.io.OutputStream;
5151
import java.util.Arrays;
5252

53-
import com.oracle.truffle.api.library.ExportLibrary;
54-
import com.oracle.truffle.api.library.ExportMessage;
5553
import org.graalvm.wasm.api.Vector128;
5654
import org.graalvm.wasm.exception.Failure;
5755
import org.graalvm.wasm.exception.WasmException;
5856

5957
import com.oracle.truffle.api.CompilerDirectives;
6058
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
59+
import com.oracle.truffle.api.library.ExportLibrary;
60+
import com.oracle.truffle.api.library.ExportMessage;
6161
import com.oracle.truffle.api.memory.ByteArraySupport;
6262
import com.oracle.truffle.api.nodes.Node;
6363

@@ -70,7 +70,7 @@ final class ByteArrayWasmMemory extends WasmMemory {
7070
@TruffleBoundary
7171
private ByteArrayWasmMemory(long declaredMinSize, long declaredMaxSize, long initialSize, long maxAllowedSize, boolean indexType64) {
7272
super(declaredMinSize, declaredMaxSize, initialSize, maxAllowedSize, indexType64, false);
73-
this.dynamicBuffer = allocateStatic(initialSize * MEMORY_PAGE_SIZE);
73+
this.dynamicBuffer = allocateBuffer(initialSize * MEMORY_PAGE_SIZE);
7474
}
7575

7676
@TruffleBoundary
@@ -100,14 +100,23 @@ public synchronized long grow(long extraPageSize) {
100100
invokeGrowCallback();
101101
return previousSize;
102102
} else if (compareUnsigned(extraPageSize, maxAllowedSize()) <= 0 && compareUnsigned(previousSize + extraPageSize, maxAllowedSize()) <= 0) {
103-
// Condition above and limit on maxAllowedSize (see
104-
// ByteArrayWasmMemory#MAX_ALLOWED_SIZE) ensure computation of targetByteSize does not
105-
// overflow.
106-
final long targetByteSize = multiplyExact(addExact(previousSize, extraPageSize), MEMORY_PAGE_SIZE);
103+
/*
104+
* Condition above and limit on maxAllowedSize (see
105+
* ByteArrayWasmMemory#MAX_ALLOWED_SIZE) ensure computation of targetByteSize does not
106+
* overflow.
107+
*/
108+
final long targetPageSize = addExact(previousSize, extraPageSize);
109+
final long targetByteSize = multiplyExact(targetPageSize, MEMORY_PAGE_SIZE);
107110
final byte[] currentBuffer = buffer();
108-
allocate(targetByteSize);
109-
System.arraycopy(currentBuffer, 0, buffer(), 0, currentBuffer.length);
110-
currentMinSize = previousSize + extraPageSize;
111+
final byte[] newBuffer;
112+
try {
113+
newBuffer = allocateBuffer(targetByteSize);
114+
} catch (WasmException oome) {
115+
return -1;
116+
}
117+
System.arraycopy(currentBuffer, 0, newBuffer, 0, currentBuffer.length);
118+
dynamicBuffer = newBuffer;
119+
currentMinSize = targetPageSize;
111120
invokeGrowCallback();
112121
return previousSize;
113122
} else {
@@ -118,7 +127,7 @@ public synchronized long grow(long extraPageSize) {
118127
@ExportMessage
119128
@TruffleBoundary
120129
public void reset() {
121-
allocate(declaredMinSize * MEMORY_PAGE_SIZE);
130+
dynamicBuffer = allocateBuffer(declaredMinSize * MEMORY_PAGE_SIZE);
122131
currentMinSize = declaredMinSize;
123132
}
124133

@@ -1090,17 +1099,9 @@ public void copyToBuffer(Node node, byte[] dst, long srcOffset, int dstOffset, i
10901099
}
10911100

10921101
@TruffleBoundary
1093-
private void allocate(long byteSize) {
1094-
dynamicBuffer = null;
1095-
dynamicBuffer = allocateStatic(byteSize);
1096-
}
1097-
1098-
@TruffleBoundary
1099-
private static byte[] allocateStatic(long byteSize) {
1100-
assert byteSize <= Integer.MAX_VALUE : byteSize;
1101-
final int effectiveByteSize = (int) byteSize;
1102+
private static byte[] allocateBuffer(long byteSize) {
11021103
try {
1103-
return new byte[effectiveByteSize];
1104+
return new byte[Math.toIntExact(byteSize)];
11041105
} catch (OutOfMemoryError error) {
11051106
throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED);
11061107
}

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/NativeWasmMemory.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ private static long allocate(long newBufferSize) {
113113
}
114114
}
115115

116+
@TruffleBoundary
117+
private long reallocate(long newBufferSize) {
118+
try {
119+
final long address = unsafe.reallocateMemory(startAddress, newBufferSize);
120+
unsafe.setMemory(address + bufferSize, newBufferSize - bufferSize, (byte) 0);
121+
return address;
122+
} catch (OutOfMemoryError error) {
123+
throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED);
124+
}
125+
}
126+
116127
private static Deallocator registerDeallocator(NativeWasmMemory memory, MemoryContext memoryContext, long address) {
117128
var deallocator = new Deallocator(address);
118129
memoryContext.registerCleaner(memory, deallocator);
@@ -139,29 +150,27 @@ public synchronized long grow(long extraPageSize) {
139150
} else if (Long.compareUnsigned(extraPageSize, maxAllowedSize()) <= 0 && Long.compareUnsigned(previousSize + extraPageSize, maxAllowedSize()) <= 0) {
140151
// Condition above and limit on maxAllowedSize (see NativeWasmMemory#MAX_ALLOWED_SIZE)
141152
// ensure computation of targetByteSize does not overflow.
142-
final long targetByteSize = Math.multiplyExact(Math.addExact(previousSize, extraPageSize), MEMORY_PAGE_SIZE);
153+
final long targetPageSize = Math.addExact(previousSize, extraPageSize);
154+
final long targetByteSize = Math.multiplyExact(targetPageSize, MEMORY_PAGE_SIZE);
143155
if (Long.compareUnsigned(targetByteSize, bufferSize) > 0) {
144156
try {
145157
long newBufferSize = newBufferSize(targetByteSize);
146-
startAddress = updateDeallocatorAddress(unsafe.reallocateMemory(startAddress, newBufferSize));
147-
unsafe.setMemory(startAddress + bufferSize, newBufferSize - bufferSize, (byte) 0);
158+
startAddress = updateDeallocatorAddress(reallocate(newBufferSize));
148159
bufferSize = newBufferSize;
149-
} catch (OutOfMemoryError error) {
160+
} catch (WasmException error) {
150161
// Over-allocating failed, so try to allocate at least the amount of memory that
151162
// was requested.
152163
try {
153164
long newBufferSize = targetByteSize;
154-
startAddress = updateDeallocatorAddress(unsafe.reallocateMemory(startAddress, newBufferSize));
155-
unsafe.setMemory(startAddress + bufferSize, newBufferSize - bufferSize, (byte) 0);
165+
startAddress = updateDeallocatorAddress(reallocate(newBufferSize));
156166
bufferSize = newBufferSize;
157-
} catch (OutOfMemoryError errorAgain) {
158-
throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED);
167+
} catch (WasmException errorAgain) {
168+
return -1;
159169
}
160170
}
161171
}
162-
final long updatedSize = previousSize + extraPageSize;
163-
currentMinSize = updatedSize;
164-
SIZE_FIELD.setVolatile(this, updatedSize);
172+
currentMinSize = targetPageSize;
173+
SIZE_FIELD.setVolatile(this, targetPageSize);
165174
invokeGrowCallback();
166175
return previousSize;
167176
} else {

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@
5454
import java.nio.Buffer;
5555
import java.nio.ByteBuffer;
5656

57-
import com.oracle.truffle.api.library.ExportLibrary;
58-
import com.oracle.truffle.api.library.ExportMessage;
5957
import org.graalvm.wasm.api.Vector128;
6058
import org.graalvm.wasm.exception.Failure;
6159
import org.graalvm.wasm.exception.WasmException;
6260

6361
import com.oracle.truffle.api.CompilerDirectives;
6462
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
63+
import com.oracle.truffle.api.library.ExportLibrary;
64+
import com.oracle.truffle.api.library.ExportMessage;
6565
import com.oracle.truffle.api.nodes.Node;
6666

6767
import sun.misc.Unsafe;
@@ -99,9 +99,8 @@ private UnsafeWasmMemory(long declaredMinSize, long declaredMaxSize, long initia
9999

100100
@TruffleBoundary
101101
private static ByteBuffer allocateBuffer(final long byteSize) {
102-
assert (int) byteSize == byteSize : byteSize;
103102
try {
104-
return ByteBuffer.allocateDirect((int) byteSize);
103+
return ByteBuffer.allocateDirect(Math.toIntExact(byteSize));
105104
} catch (OutOfMemoryError error) {
106105
throw WasmException.create(Failure.MEMORY_ALLOCATION_FAILED);
107106
}
@@ -149,7 +148,12 @@ public synchronized long grow(long extraPageSize) {
149148
final long targetByteSize = multiplyExact(addExact(previousSize, extraPageSize), MEMORY_PAGE_SIZE);
150149
if (compareUnsigned(targetByteSize, buffer.capacity()) > 0) {
151150
final long sourceByteSize = byteSize();
152-
ByteBuffer updatedBuffer = allocateBuffer(newBufferSize(targetByteSize));
151+
ByteBuffer updatedBuffer;
152+
try {
153+
updatedBuffer = allocateBuffer(newBufferSize(targetByteSize));
154+
} catch (WasmException oome) {
155+
return -1;
156+
}
153157
final long updatedStartAddress = getBufferAddress(updatedBuffer);
154158
unsafe.copyMemory(startAddress, updatedStartAddress, sourceByteSize);
155159
buffer = updatedBuffer;

0 commit comments

Comments
 (0)