Skip to content

Commit 3f0ad61

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
Reland "[vm/ffi] Disallow Pointers and structs in finalizers and expandos"
`Dart_NewWeakPersistentHandle` and `Dart_NewFinalizableHandle` in `dart_api.h` do no longer accept `Pointer`s and subtypes of `Struct`s as values passed in to the `object` parameter. Expandos no longer accept `Pointer`s and subtypes of `Struct`s as values passed in to the `object` parameter. Cleans up unused object_store->ffi_struct_class. Closes: #45071 Breaking change: #45072 TEST=vm/cc/DartAPI_FinalizableHandleErrors TEST=vm/cc/DartAPI_WeakPersistentHandleErrors TEST=tests/ffi(_2)/expando_test.dart Change-Id: I9af6d0173db60614091068c218391f73756c135f Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195061 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Daco Harkes <[email protected]>
1 parent 8800dec commit 3f0ad61

File tree

14 files changed

+221
-31
lines changed

14 files changed

+221
-31
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
daylight saving changes that are not precisely one hour.
99
(No change on the Web which uses the JavaScript `Date` object.)
1010

11+
### Dart VM
12+
13+
* **Breaking Change** [#45071][]: `Dart_NewWeakPersistentHandle`'s and
14+
`Dart_NewFinalizableHandle`'s `object` parameter no longer accepts
15+
`Pointer`s and subtypes of `Struct`. Expandos no longer accept
16+
`Pointer`s and subtypes of `Struct`s.
17+
18+
[#45071]: https://github.com/dart-lang/sdk/issues/45071
19+
1120
## 2.13.0
1221

1322
### Language

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
834834
: null;
835835
}
836836

837-
if (!nativeTypesClasses.contains(klass) && klass != arrayClass) {
837+
if (!nativeTypesClasses.contains(klass) &&
838+
klass != arrayClass &&
839+
klass != arraySizeClass) {
838840
for (final parent in nativeTypesClasses) {
839841
if (hierarchy.isSubtypeOf(klass, parent)) {
840842
return parent;

runtime/include/dart_api.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
475475
*
476476
* Requires there to be a current isolate.
477477
*
478-
* \param object An object.
478+
* \param object An object with identity.
479479
* \param peer A pointer to a native object or NULL. This value is
480480
* provided to callback when it is invoked.
481481
* \param external_allocation_size The number of externally allocated
@@ -531,7 +531,7 @@ DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
531531
*
532532
* Requires there to be a current isolate.
533533
*
534-
* \param object An object.
534+
* \param object An object with identity.
535535
* \param peer A pointer to a native object or NULL. This value is
536536
* provided to callback when it is invoked.
537537
* \param external_allocation_size The number of externally allocated

runtime/vm/dart_api_impl.cc

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,23 @@ DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,
991991
obj1_ref->set_ptr(obj2_ref);
992992
}
993993

994+
// TODO(https://dartbug.com/38491): Reject Unions here as well.
995+
static bool IsFfiStruct(Thread* T, const Object& obj) {
996+
if (obj.IsNull()) {
997+
return false;
998+
}
999+
1000+
// CFE guarantees we can only have direct subclasses of `Struct`
1001+
// (no implementations or indirect subclasses are allowed).
1002+
const auto& klass = Class::Handle(Z, obj.clazz());
1003+
const auto& super_klass = Class::Handle(Z, klass.SuperClass());
1004+
if (super_klass.Name() != Symbols::Struct().ptr()) {
1005+
return false;
1006+
}
1007+
const auto& library = Library::Handle(Z, super_klass.library());
1008+
return library.url() == Symbols::DartFfi().ptr();
1009+
}
1010+
9941011
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
9951012
Thread* thread,
9961013
const Object& ref,
@@ -1000,6 +1017,13 @@ static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
10001017
if (!ref.ptr()->IsHeapObject()) {
10011018
return NULL;
10021019
}
1020+
if (ref.IsPointer()) {
1021+
return NULL;
1022+
}
1023+
if (IsFfiStruct(thread, ref)) {
1024+
return NULL;
1025+
}
1026+
10031027
FinalizablePersistentHandle* finalizable_ref =
10041028
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
10051029
callback, external_allocation_size,
@@ -1008,16 +1032,14 @@ static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
10081032
}
10091033

10101034
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
1011-
Thread* thread,
1035+
Thread* T,
10121036
Dart_Handle object,
10131037
void* peer,
10141038
intptr_t external_allocation_size,
10151039
Dart_HandleFinalizer callback) {
1016-
REUSABLE_OBJECT_HANDLESCOPE(thread);
1017-
Object& ref = thread->ObjectHandle();
1018-
ref = Api::UnwrapHandle(object);
1019-
return AllocateWeakPersistentHandle(thread, ref, peer,
1020-
external_allocation_size, callback);
1040+
const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
1041+
return AllocateWeakPersistentHandle(T, ref, peer, external_allocation_size,
1042+
callback);
10211043
}
10221044

10231045
static Dart_FinalizableHandle AllocateFinalizableHandle(
@@ -1029,6 +1051,12 @@ static Dart_FinalizableHandle AllocateFinalizableHandle(
10291051
if (!ref.ptr()->IsHeapObject()) {
10301052
return NULL;
10311053
}
1054+
if (ref.IsPointer()) {
1055+
return NULL;
1056+
}
1057+
if (IsFfiStruct(thread, ref)) {
1058+
return NULL;
1059+
}
10321060

10331061
FinalizablePersistentHandle* finalizable_ref =
10341062
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
@@ -1038,15 +1066,13 @@ static Dart_FinalizableHandle AllocateFinalizableHandle(
10381066
}
10391067

10401068
static Dart_FinalizableHandle AllocateFinalizableHandle(
1041-
Thread* thread,
1069+
Thread* T,
10421070
Dart_Handle object,
10431071
void* peer,
10441072
intptr_t external_allocation_size,
10451073
Dart_HandleFinalizer callback) {
1046-
REUSABLE_OBJECT_HANDLESCOPE(thread);
1047-
Object& ref = thread->ObjectHandle();
1048-
ref = Api::UnwrapHandle(object);
1049-
return AllocateFinalizableHandle(thread, ref, peer, external_allocation_size,
1074+
const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
1075+
return AllocateFinalizableHandle(T, ref, peer, external_allocation_size,
10501076
callback);
10511077
}
10521078

@@ -1055,30 +1081,27 @@ Dart_NewWeakPersistentHandle(Dart_Handle object,
10551081
void* peer,
10561082
intptr_t external_allocation_size,
10571083
Dart_HandleFinalizer callback) {
1058-
Thread* thread = Thread::Current();
1059-
CHECK_ISOLATE(thread->isolate());
1084+
DARTSCOPE(Thread::Current());
10601085
if (callback == NULL) {
10611086
return NULL;
10621087
}
1063-
TransitionNativeToVM transition(thread);
10641088

1065-
return AllocateWeakPersistentHandle(thread, object, peer,
1066-
external_allocation_size, callback);
1089+
return AllocateWeakPersistentHandle(T, object, peer, external_allocation_size,
1090+
callback);
10671091
}
10681092

10691093
DART_EXPORT Dart_FinalizableHandle
10701094
Dart_NewFinalizableHandle(Dart_Handle object,
10711095
void* peer,
10721096
intptr_t external_allocation_size,
10731097
Dart_HandleFinalizer callback) {
1074-
Thread* thread = Thread::Current();
1075-
CHECK_ISOLATE(thread->isolate());
1098+
DARTSCOPE(Thread::Current());
10761099
if (callback == nullptr) {
10771100
return nullptr;
10781101
}
1079-
TransitionNativeToVM transition(thread);
1080-
return AllocateFinalizableHandle(thread, object, peer,
1081-
external_allocation_size, callback);
1102+
1103+
return AllocateFinalizableHandle(T, object, peer, external_allocation_size,
1104+
callback);
10821105
}
10831106

10841107
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,

runtime/vm/dart_api_impl_test.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3368,6 +3368,35 @@ TEST_CASE(DartAPI_WeakPersistentHandleErrors) {
33683368
Dart_NewWeakPersistentHandle(obj2, NULL, 0, NopCallback);
33693369
EXPECT_EQ(ref2, static_cast<void*>(NULL));
33703370

3371+
// Pointer object.
3372+
Dart_Handle ffi_lib = Dart_LookupLibrary(NewString("dart:ffi"));
3373+
Dart_Handle pointer_type =
3374+
Dart_GetNonNullableType(ffi_lib, NewString("Pointer"), 0, NULL);
3375+
Dart_Handle obj3 = Dart_Allocate(pointer_type);
3376+
EXPECT_VALID(obj3);
3377+
Dart_WeakPersistentHandle ref3 =
3378+
Dart_NewWeakPersistentHandle(obj3, nullptr, 0, FinalizableHandleCallback);
3379+
EXPECT_EQ(ref3, static_cast<void*>(nullptr));
3380+
3381+
// Subtype of Struct object.
3382+
const char* kScriptChars = R"(
3383+
import 'dart:ffi';
3384+
3385+
class MyStruct extends Struct {
3386+
external Pointer notEmpty;
3387+
}
3388+
)";
3389+
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
3390+
Dart_Handle my_struct_type =
3391+
Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
3392+
Dart_Handle obj4 = Dart_Allocate(my_struct_type);
3393+
EXPECT_VALID(obj4);
3394+
Dart_WeakPersistentHandle ref4 =
3395+
Dart_NewWeakPersistentHandle(obj4, nullptr, 0, FinalizableHandleCallback);
3396+
EXPECT_EQ(ref4, static_cast<void*>(nullptr));
3397+
3398+
// TODO(https://dartbug.com/38491): Reject Unions here as well.
3399+
33713400
Dart_ExitScope();
33723401
}
33733402

@@ -3388,6 +3417,33 @@ TEST_CASE(DartAPI_FinalizableHandleErrors) {
33883417
Dart_NewFinalizableHandle(obj2, nullptr, 0, FinalizableHandleCallback);
33893418
EXPECT_EQ(ref2, static_cast<void*>(nullptr));
33903419

3420+
// Pointer object.
3421+
Dart_Handle ffi_lib = Dart_LookupLibrary(NewString("dart:ffi"));
3422+
Dart_Handle pointer_type =
3423+
Dart_GetNonNullableType(ffi_lib, NewString("Pointer"), 0, NULL);
3424+
Dart_Handle obj3 = Dart_Allocate(pointer_type);
3425+
EXPECT_VALID(obj3);
3426+
Dart_FinalizableHandle ref3 =
3427+
Dart_NewFinalizableHandle(obj3, nullptr, 0, FinalizableHandleCallback);
3428+
EXPECT_EQ(ref3, static_cast<void*>(nullptr));
3429+
3430+
// Subtype of Struct object.
3431+
const char* kScriptChars = R"(
3432+
import 'dart:ffi';
3433+
3434+
class MyStruct extends Struct {
3435+
external Pointer notEmpty;
3436+
}
3437+
)";
3438+
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
3439+
Dart_Handle my_struct_type =
3440+
Dart_GetNonNullableType(lib, NewString("MyStruct"), 0, NULL);
3441+
Dart_Handle obj4 = Dart_Allocate(my_struct_type);
3442+
EXPECT_VALID(obj4);
3443+
Dart_FinalizableHandle ref4 =
3444+
Dart_NewFinalizableHandle(obj4, nullptr, 0, FinalizableHandleCallback);
3445+
EXPECT_EQ(ref4, static_cast<void*>(nullptr));
3446+
33913447
Dart_ExitScope();
33923448
}
33933449

runtime/vm/kernel_loader.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,8 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
13671367
"runtime");
13681368
}
13691369
if (!Api::IsFfiEnabled() &&
1370-
target_library.url() == Symbols::DartFfi().ptr()) {
1370+
target_library.url() == Symbols::DartFfi().ptr() &&
1371+
library->url() != Symbols::DartCore().ptr()) {
13711372
H.ReportError(
13721373
"import of dart:ffi is not supported in the current Dart runtime");
13731374
}

runtime/vm/object_store.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,6 @@ class ObjectPointerVisitor;
236236
RW(GrowableObjectArray, ffi_callback_functions) \
237237
RW(Class, ffi_pointer_class) \
238238
RW(Class, ffi_native_type_class) \
239-
RW(Class, ffi_struct_class) \
240239
RW(Object, ffi_as_function_internal) \
241240
// Please remember the last entry must be referred in the 'to' function below.
242241

sdk/lib/_internal/vm/lib/core_patch.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import "dart:collection"
4949

5050
import "dart:convert" show ascii, Encoding, json, latin1, utf8;
5151

52+
import "dart:ffi" show Pointer, Struct;
53+
5254
import "dart:isolate" show Isolate;
5355

5456
import "dart:math" show Random;

sdk/lib/_internal/vm/lib/expando_patch.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,12 @@ class Expando<T> {
140140
if ((object == null) ||
141141
(object is bool) ||
142142
(object is num) ||
143-
(object is String)) {
143+
(object is String) ||
144+
(object is Pointer) ||
145+
(object is Struct)) {
146+
// TODO(https://dartbug.com/38491): Reject Unions here as well.
144147
throw new ArgumentError.value(object,
145-
"Expandos are not allowed on strings, numbers, booleans or null");
148+
"Expandos are not allowed on strings, numbers, booleans, null, Pointers or Structs.");
146149
}
147150
}
148151

sdk/lib/core/expando.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ part of dart.core;
66

77
/// An [Expando] allows adding new properties to objects.
88
///
9-
/// Does not work on numbers, strings, booleans or `null`.
9+
/// Does not work on numbers, strings, booleans, `null`, `dart:ffi` pointers
10+
/// or `dart:ffi` structs.
1011
///
1112
/// An `Expando` does not hold on to the added property value after an object
1213
/// becomes inaccessible.
@@ -38,13 +39,15 @@ class Expando<T extends Object> {
3839
/// object. If the object hasn't been expanded, the method returns
3940
/// `null`.
4041
///
41-
/// The object must not be a number, a string or a boolean.
42+
/// The object must not be a number, a string, a boolean, `null`, a
43+
/// `dart:ffi` pointer, or a `dart:ffi` struct.
4244
external T? operator [](Object object);
4345

4446
/// Sets the value of this [Expando]'s property on the given
4547
/// object. Properties can effectively be removed again by setting
4648
/// their value to `null`.
4749
///
48-
/// The object must not be a number, a string or a boolean.
50+
/// The object must not be a number, a string, a boolean, `null`, a
51+
/// `dart:ffi` pointer, or a `dart:ffi` struct.
4952
external void operator []=(Object object, T? value);
5053
}

tests/corelib/expando_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// VMOptions=--enable-ffi=true
6+
// VMOptions=--enable-ffi=false
47

58
import "package:expect/expect.dart";
69

tests/corelib_2/expando_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// VMOptions=--enable-ffi=true
6+
// VMOptions=--enable-ffi=false
47

58
import "package:expect/expect.dart";
69

tests/ffi/expando_test.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// Dart test program for testing dart:ffi primitive data pointers.
6+
//
7+
// SharedObjects=ffi_test_functions
8+
9+
import 'dart:ffi';
10+
11+
import "package:expect/expect.dart";
12+
13+
void main() {
14+
testPointer();
15+
testStruct();
16+
}
17+
18+
Expando<int> expando = Expando('myExpando');
19+
20+
void testPointer() {
21+
final pointer = Pointer<Int8>.fromAddress(0xdeadbeef);
22+
Expect.throws(() {
23+
expando[pointer];
24+
});
25+
Expect.throws(() {
26+
expando[pointer] = 1234;
27+
});
28+
}
29+
30+
class MyStruct extends Struct {
31+
external Pointer notEmpty;
32+
}
33+
34+
void testStruct() {
35+
final pointer = Pointer<MyStruct>.fromAddress(0xdeadbeef);
36+
final struct = pointer.ref;
37+
Expect.throws(() {
38+
expando[struct];
39+
});
40+
Expect.throws(() {
41+
expando[struct] = 1234;
42+
});
43+
}

0 commit comments

Comments
 (0)