Skip to content

Commit 788cb4c

Browse files
committed
[GR-40769] Check for native access when creating a Pointer
PullRequest: truffleruby/3474
2 parents 2607892 + f94339e commit 788cb4c

File tree

19 files changed

+187
-110
lines changed

19 files changed

+187
-110
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ Changes:
5959
* Refactored sharing of array objects between threads using new `SharedArrayStorage` (@aardvark179).
6060
* Marking of native structures wrapped in objects is now done on C call exit to reduce memory overhead (@aardvark179).
6161

62+
Security:
63+
64+
* The native access permission is now properly checked before any native pointer (e.g. `Truffle::FFI::Pointer`) is created (@eregon).
65+
6266
# 22.2.0
6367

6468
New features:

src/main/java/org/truffleruby/cext/CExtNodes.java

+19-17
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.jcodings.Encoding;
2323
import org.jcodings.IntHolder;
2424
import org.truffleruby.Layouts;
25+
import org.truffleruby.RubyContext;
2526
import org.truffleruby.RubyLanguage;
2627
import org.truffleruby.builtins.CoreMethod;
2728
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
@@ -149,9 +150,9 @@ public class CExtNodes {
149150
public static final int RUBY_TAG_THROW = 0x7;
150151
public static final int RUBY_TAG_FATAL = 0x8;
151152

152-
public static Pointer newNativeStringPointer(int capacity, RubyLanguage language) {
153+
public static Pointer newNativeStringPointer(RubyLanguage language, RubyContext context, int capacity) {
153154
// We need up to 4 \0 bytes for UTF-32. Always use 4 for speed rather than checking the encoding min length.
154-
Pointer pointer = Pointer.mallocAutoRelease(capacity + 4, language);
155+
Pointer pointer = Pointer.mallocAutoRelease(language, context, capacity + 4);
155156
pointer.writeInt(capacity, 0);
156157
return pointer;
157158
}
@@ -765,7 +766,7 @@ public abstract static class RbStrNewNulNode extends CoreMethodArrayArgumentsNod
765766
@Specialization
766767
protected RubyString rbStrNewNul(int byteLength,
767768
@Cached MutableTruffleString.FromNativePointerNode fromNativePointerNode) {
768-
final Pointer pointer = Pointer.callocAutoRelease(byteLength + 1, getLanguage());
769+
final Pointer pointer = Pointer.callocAutoRelease(getLanguage(), getContext(), byteLength + 1);
769770
var nativeTString = fromNativePointerNode.execute(pointer, 0, byteLength, Encodings.BINARY.tencoding,
770771
false);
771772
return createMutableString(nativeTString, Encodings.BINARY);
@@ -842,8 +843,8 @@ protected RubyString rbStrResize(RubyString string, int newByteLength,
842843
string.clearCodeRange();
843844
return string;
844845
} else {
845-
var newNativeTString = TrStrCapaResizeNode.resize(pointer, newByteLength, newByteLength, tencoding,
846-
fromNativePointerNode, getLanguage());
846+
var newNativeTString = TrStrCapaResizeNode.resize(getLanguage(), getContext(), pointer, newByteLength,
847+
newByteLength, tencoding, fromNativePointerNode);
847848
string.setTString(newNativeTString);
848849

849850
// Like MRI's rb_str_resize()
@@ -869,18 +870,18 @@ protected RubyString trStrCapaResize(RubyString string, int newCapacity,
869870
return string;
870871
} else {
871872
int byteLength = string.tstring.byteLength(tencoding);
872-
var newNativeTString = resize(pointer, newCapacity, byteLength, tencoding, fromNativePointerNode,
873-
getLanguage());
873+
var newNativeTString = resize(getLanguage(), getContext(), pointer, newCapacity, byteLength, tencoding,
874+
fromNativePointerNode);
874875
string.setTString(newNativeTString);
875876

876877
return string;
877878
}
878879
}
879880

880-
static MutableTruffleString resize(Pointer pointer, int newCapacity, int newByteLength,
881-
TruffleString.Encoding tencoding, MutableTruffleString.FromNativePointerNode fromNativePointerNode,
882-
RubyLanguage language) {
883-
final Pointer newPointer = newNativeStringPointer(newCapacity, language);
881+
static MutableTruffleString resize(RubyLanguage language, RubyContext context, Pointer pointer, int newCapacity,
882+
int newByteLength, TruffleString.Encoding tencoding,
883+
MutableTruffleString.FromNativePointerNode fromNativePointerNode) {
884+
final Pointer newPointer = newNativeStringPointer(language, context, newCapacity);
884885
newPointer.writeBytes(0, pointer, 0, Math.min(pointer.getSize(), newCapacity));
885886

886887
return fromNativePointerNode.execute(newPointer, 0, newByteLength, tencoding, false);
@@ -1285,8 +1286,8 @@ protected Pointer toNative(RubyString string,
12851286
pointer = (Pointer) getInternalNativePointerNode.execute(tstring, tencoding);
12861287
} else {
12871288
int byteLength = tstring.byteLength(tencoding);
1288-
pointer = allocateAndCopyToNative(tstring, tencoding, byteLength, copyToNativeMemoryNode,
1289-
getLanguage());
1289+
pointer = allocateAndCopyToNative(getLanguage(), getContext(), tstring, tencoding, byteLength,
1290+
copyToNativeMemoryNode);
12901291

12911292
var nativeTString = fromNativePointerNode.execute(pointer, 0, byteLength, tencoding, false);
12921293
string.setTString(nativeTString);
@@ -1300,9 +1301,10 @@ protected Pointer toNativeImmutable(ImmutableRubyString string) {
13001301
return string.getNativeString(getLanguage());
13011302
}
13021303

1303-
public static Pointer allocateAndCopyToNative(AbstractTruffleString tstring, TruffleString.Encoding tencoding,
1304-
int capacity, TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode, RubyLanguage language) {
1305-
final Pointer pointer = newNativeStringPointer(capacity, language);
1304+
public static Pointer allocateAndCopyToNative(RubyLanguage language, RubyContext context,
1305+
AbstractTruffleString tstring, TruffleString.Encoding tencoding, int capacity,
1306+
TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode) {
1307+
final Pointer pointer = newNativeStringPointer(language, context, capacity);
13061308
copyToNativeMemoryNode.execute(tstring, 0, pointer, 0, capacity, tencoding);
13071309
return pointer;
13081310
}
@@ -2026,7 +2028,7 @@ public abstract static class DataHolderCreate extends PrimitiveArrayArgumentsNod
20262028

20272029
@Specialization
20282030
protected DataHolder create(Object address) {
2029-
return new DataHolder(address, Pointer.NULL);
2031+
return new DataHolder(address, Pointer.getNullPointer(getContext()));
20302032
}
20312033
}
20322034

src/main/java/org/truffleruby/cext/DataHolder.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ public final class DataHolder implements TruffleObject {
2626
private Object pointer;
2727
private Object marker;
2828

29-
public DataHolder(Object address, Object marker) {
30-
this.pointer = address;
29+
public DataHolder(Object pointer, Object marker) {
30+
this.pointer = pointer;
3131
this.marker = marker;
3232
}
3333

3434
public Object getPointer() {
3535
return pointer;
3636
}
3737

38-
public void setPointer(Object address) {
39-
this.pointer = address;
38+
public void setPointer(Object pointer) {
39+
this.pointer = pointer;
4040
}
4141

4242
public Object getMarker() {

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -643,9 +643,9 @@ protected long argvLength() {
643643
}
644644

645645
int argc = getContext().nativeArgc;
646-
Pointer argv = new Pointer(getContext().nativeArgv, argc * Pointer.SIZE);
647-
Pointer first = argv.readPointer(0);
648-
Pointer last = argv.readPointer((argc - 1) * Pointer.SIZE);
646+
Pointer argv = new Pointer(getContext(), getContext().nativeArgv, argc * Pointer.SIZE);
647+
Pointer first = argv.readPointer(getContext(), 0);
648+
Pointer last = argv.readPointer(getContext(), (argc - 1) * Pointer.SIZE);
649649
long lastByte = last.getAddress() + last.findNullByte(getContext(), InteropLibrary.getUncached(), 0);
650650
nativeArgvLength = lastByte - first.getAddress();
651651

src/main/java/org/truffleruby/core/array/ArrayNodes.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -2325,7 +2325,7 @@ protected RubyArray storeToNative(RubyArray array,
23252325
@Cached IsSharedNode isSharedNode,
23262326
@Cached ConditionProfile sharedProfile) {
23272327
final int size = arraySizeProfile.profile(array.size);
2328-
Pointer pointer = Pointer.mallocAutoRelease(size * Pointer.SIZE, getLanguage());
2328+
Pointer pointer = Pointer.mallocAutoRelease(getLanguage(), getContext(), size * Pointer.SIZE);
23292329
Object newStore = new NativeArrayStorage(pointer, size);
23302330
stores.copyContents(store, 0, newStore, 0, size);
23312331
array.setStore(newStore);

src/main/java/org/truffleruby/core/array/library/NativeArrayStorage.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1919
import com.oracle.truffle.api.profiles.ConditionProfile;
2020
import com.oracle.truffle.api.profiles.LoopConditionProfile;
21+
import org.truffleruby.RubyContext;
2122
import org.truffleruby.cext.UnwrapNode;
2223
import org.truffleruby.cext.UnwrapNodeGen.UnwrapNativeNodeGen;
2324
import org.truffleruby.cext.ValueWrapper;
@@ -122,9 +123,10 @@ protected int capacity() {
122123
}
123124

124125
@ExportMessage
125-
protected NativeArrayStorage expand(int newCapacity) {
126+
protected NativeArrayStorage expand(int newCapacity,
127+
@CachedLibrary("this") ArrayStoreLibrary node) {
126128
final int capacity = this.length;
127-
Pointer newPointer = Pointer.malloc(capacity);
129+
Pointer newPointer = Pointer.malloc(RubyContext.get(node), capacity);
128130
newPointer.writeBytes(0, pointer, 0, capacity);
129131
newPointer.writeBytes(capacity, newCapacity - capacity, (byte) 0);
130132
/* We copy the contents of the marked objects to ensure the references will be kept alive even if the old store

src/main/java/org/truffleruby/core/encoding/EncodingManager.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private void initializeLocaleEncoding(TruffleNFIPlatform nfi, NativeConfiguratio
153153
} catch (InteropException e) {
154154
throw CompilerDirectives.shouldNotReachHere(e);
155155
}
156-
final byte[] bytes = new Pointer(address).readZeroTerminatedByteArray(
156+
final byte[] bytes = new Pointer(context, address).readZeroTerminatedByteArray(
157157
context,
158158
InteropLibrary.getUncached(),
159159
0);

src/main/java/org/truffleruby/core/format/read/bytes/ReadStringPointerNode.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected MissingValue decode(Nil nil) {
4848
protected RubyString read(VirtualFrame frame, long address,
4949
@CachedLibrary(limit = "getRubyLibraryCacheLimit()") RubyLibrary rubyLibrary,
5050
@CachedLibrary(limit = "1") InteropLibrary interop) {
51-
final Pointer pointer = new Pointer(address);
51+
final Pointer pointer = new Pointer(getContext(), address);
5252
checkAssociated(
5353
(Pointer[]) frame.getObject(FormatFrameDescriptor.SOURCE_ASSOCIATED_SLOT),
5454
pointer);

src/main/java/org/truffleruby/core/string/ImmutableRubyString.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ private synchronized Pointer createNativeString(RubyLanguage language) {
8484
if (nativeString == null) {
8585
var tencoding = getEncodingUncached().tencoding;
8686
int byteLength = tstring.byteLength(tencoding);
87-
nativeString = CExtNodes.StringToNativeNode.allocateAndCopyToNative(tstring, tencoding, byteLength,
88-
TruffleString.CopyToNativeMemoryNode.getUncached(), language);
87+
nativeString = CExtNodes.StringToNativeNode.allocateAndCopyToNative(language,
88+
RubyLanguage.getCurrentContext(), tstring, tencoding, byteLength,
89+
TruffleString.CopyToNativeMemoryNode.getUncached());
8990
}
9091
return nativeString;
9192
}

src/main/java/org/truffleruby/core/string/StringNodes.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ protected Object initializeCopyNative(RubyString self, RubyString from,
15891589
var tencoding = encoding.tencoding;
15901590
final Pointer fromPointer = (Pointer) getInternalNativePointerNode.execute(tstring, tencoding);
15911591

1592-
final Pointer newPointer = Pointer.mallocAutoRelease(fromPointer.getSize(), getLanguage());
1592+
final Pointer newPointer = Pointer.mallocAutoRelease(getLanguage(), getContext(), fromPointer.getSize());
15931593
newPointer.writeBytes(0, fromPointer, 0, fromPointer.getSize());
15941594

15951595
// TODO (eregon, 2022): should we have the copy be native too, or rather take the opportunity of having to copy to be managed?

src/main/java/org/truffleruby/core/support/IONodes.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import java.util.Arrays;
6868

6969
import com.oracle.truffle.api.strings.TruffleString;
70+
import org.truffleruby.RubyContext;
7071
import org.truffleruby.builtins.CoreMethod;
7172
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
7273
import org.truffleruby.builtins.CoreModule;
@@ -515,13 +516,14 @@ protected RubyPointer getThreadBuffer(long size,
515516
final RubyPointer instance = new RubyPointer(
516517
coreLibrary().truffleFFIPointerClass,
517518
getLanguage().truffleFFIPointerShape,
518-
getBuffer(thread, size, sizeProfile));
519+
getBuffer(getContext(), thread, size, sizeProfile));
519520
AllocationTracing.trace(instance, this);
520521
return instance;
521522
}
522523

523-
public static Pointer getBuffer(RubyThread rubyThread, long size, ConditionProfile sizeProfile) {
524-
return rubyThread.ioBuffer.allocate(rubyThread, size, sizeProfile);
524+
public static Pointer getBuffer(RubyContext context, RubyThread rubyThread, long size,
525+
ConditionProfile sizeProfile) {
526+
return rubyThread.getIoBuffer(context).allocate(context, rubyThread, size, sizeProfile);
525527
}
526528
}
527529

@@ -532,7 +534,7 @@ public abstract static class IOThreadBufferFreeNode extends PrimitiveArrayArgume
532534
protected Object getThreadBuffer(RubyPointer pointer,
533535
@Cached ConditionProfile freeProfile) {
534536
RubyThread thread = getLanguage().getCurrentThread();
535-
thread.ioBuffer.free(thread, pointer.pointer, freeProfile);
537+
thread.getIoBuffer(getContext()).free(thread, pointer.pointer, freeProfile);
536538
return nil;
537539
}
538540
}

src/main/java/org/truffleruby/core/thread/RubyThread.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.truffleruby.core.support.PRNGRandomizerNodes;
3232
import org.truffleruby.core.support.RubyPRNGRandomizer;
3333
import org.truffleruby.core.tracepoint.TracePointState;
34+
import org.truffleruby.extra.ffi.Pointer;
3435
import org.truffleruby.language.Nil;
3536
import org.truffleruby.language.RubyDynamicObject;
3637
import org.truffleruby.language.objects.ObjectGraph;
@@ -62,7 +63,7 @@ public final class RubyThread extends RubyDynamicObject implements ObjectGraphNo
6263
volatile Object value = null;
6364
public final AtomicBoolean wakeUp = new AtomicBoolean(false);
6465
volatile int priority = Thread.NORM_PRIORITY;
65-
public ThreadLocalBuffer ioBuffer = ThreadLocalBuffer.NULL_BUFFER;
66+
ThreadLocalBuffer ioBuffer;
6667
Object threadGroup;
6768
public String sourceLocation;
6869
Object name = Nil.INSTANCE;
@@ -132,6 +133,11 @@ public void setCurrentFiber(RubyFiber fiber) {
132133
currentFiber = fiber;
133134
}
134135

136+
public ThreadLocalBuffer getIoBuffer(RubyContext context) {
137+
Pointer.checkNativeAccess(context);
138+
return ioBuffer;
139+
}
140+
135141
@Override
136142
public void getAdjacentObjects(Set<Object> reachable) {
137143
ObjectGraph.addProperty(reachable, threadLocalVariables);

src/main/java/org/truffleruby/core/thread/ThreadLocalBuffer.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1313
import com.oracle.truffle.api.profiles.ConditionProfile;
1414

15+
import org.truffleruby.RubyContext;
1516
import org.truffleruby.extra.ffi.Pointer;
1617

1718
public final class ThreadLocalBuffer {
1819

19-
public static final ThreadLocalBuffer NULL_BUFFER = new ThreadLocalBuffer(new Pointer(0, 0), null);
2020
private static final long ALIGNMENT = 8L;
2121
private static final long ALIGNMENT_MASK = ALIGNMENT - 1;
2222

2323
public final Pointer start;
2424
long remaining;
2525
private final ThreadLocalBuffer parent;
2626

27-
private ThreadLocalBuffer(Pointer start, ThreadLocalBuffer parent) {
27+
public ThreadLocalBuffer(Pointer start, ThreadLocalBuffer parent) {
2828
this.start = start;
2929
this.remaining = start.getSize();
3030
this.parent = parent;
@@ -61,14 +61,14 @@ public void free(RubyThread thread, Pointer ptr, ConditionProfile freeProfile) {
6161

6262
public void freeAll(RubyThread thread) {
6363
ThreadLocalBuffer current = this;
64-
thread.ioBuffer = NULL_BUFFER;
64+
thread.ioBuffer = null;
6565
while (current != null) {
6666
current.freeMemory();
6767
current = current.parent;
6868
}
6969
}
7070

71-
public Pointer allocate(RubyThread thread, long size, ConditionProfile allocationProfile) {
71+
public Pointer allocate(RubyContext context, RubyThread thread, long size, ConditionProfile allocationProfile) {
7272
/* If there is space in the thread's existing buffer then we will return a pointer to that and reduce the
7373
* remaining space count. Otherwise we will either allocate a new buffer, or (if no space is currently being
7474
* used in the existing buffer) replace it with a larger one. */
@@ -77,13 +77,13 @@ public Pointer allocate(RubyThread thread, long size, ConditionProfile allocatio
7777
* or reallocating a buffer that we technically have a pointer to. */
7878
final long allocationSize = alignUp(size);
7979
if (allocationProfile.profile(remaining >= allocationSize)) {
80-
final Pointer pointer = new Pointer(cursor(), allocationSize);
80+
final Pointer pointer = new Pointer(context, cursor(), allocationSize);
8181
remaining -= allocationSize;
8282
assert invariants();
8383
return pointer;
8484
} else {
85-
final ThreadLocalBuffer newBuffer = allocateNewBlock(thread, allocationSize);
86-
final Pointer pointer = new Pointer(newBuffer.start.getAddress(), allocationSize);
85+
final ThreadLocalBuffer newBuffer = allocateNewBlock(context, thread, allocationSize);
86+
final Pointer pointer = new Pointer(context, newBuffer.start.getAddress(), allocationSize);
8787
newBuffer.remaining -= allocationSize;
8888
assert newBuffer.invariants();
8989
return pointer;
@@ -95,17 +95,17 @@ private static long alignUp(long size) {
9595
}
9696

9797
@TruffleBoundary
98-
private ThreadLocalBuffer allocateNewBlock(RubyThread thread, long size) {
98+
private ThreadLocalBuffer allocateNewBlock(RubyContext context, RubyThread thread, long size) {
9999
// Allocate a new buffer. Chain it if we aren't the default thread buffer, otherwise make a new default buffer.
100100
final long blockSize = Math.max(size, 1024);
101101
final ThreadLocalBuffer newBuffer;
102102
if (this.parent == null && this.isEmpty()) {
103103
// Free the old block
104104
freeMemory();
105105
// Create new bigger block
106-
newBuffer = new ThreadLocalBuffer(Pointer.malloc(blockSize), null);
106+
newBuffer = new ThreadLocalBuffer(Pointer.malloc(context, blockSize), null);
107107
} else {
108-
newBuffer = new ThreadLocalBuffer(Pointer.malloc(blockSize), this);
108+
newBuffer = new ThreadLocalBuffer(Pointer.malloc(context, blockSize), this);
109109
}
110110
thread.ioBuffer = newBuffer;
111111
return newBuffer;

src/main/java/org/truffleruby/core/thread/ThreadManager.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.truffleruby.core.klass.RubyClass;
4242
import org.truffleruby.core.string.StringUtils;
4343
import org.truffleruby.core.support.PRNGRandomizerNodes;
44+
import org.truffleruby.extra.ffi.Pointer;
4445
import org.truffleruby.interop.InteropNodes;
4546
import org.truffleruby.interop.TranslateInteropExceptionNode;
4647
import org.truffleruby.language.Nil;
@@ -375,6 +376,7 @@ public void startForeignThread(RubyThread rubyThread, Thread javaThread) {
375376

376377
public void start(RubyThread thread, Thread javaThread) {
377378
thread.thread = javaThread;
379+
thread.ioBuffer = context.getEnv().isNativeAccessAllowed() ? Pointer.getNullBuffer(context) : null;
378380
registerThread(thread);
379381

380382
final RubyFiber rootFiber = thread.getRootFiber();
@@ -397,7 +399,9 @@ private void cleanupKillOtherFibers(RubyThread thread) {
397399
public void cleanupThreadState(RubyThread thread, Thread javaThread) {
398400
context.fiberManager.cleanup(thread.getRootFiber(), javaThread);
399401

400-
thread.ioBuffer.freeAll(thread);
402+
if (thread.ioBuffer != null) {
403+
thread.ioBuffer.freeAll(thread);
404+
}
401405

402406
unregisterThread(thread);
403407
thread.thread = null;

0 commit comments

Comments
 (0)