Skip to content

Commit 8c5cda8

Browse files
bors[bot]Bromeon
andauthored
Merge #795
795: Remove or change APIs causing UB r=Bromeon a=Bromeon Fixes #790 Changes in commit message Co-authored-by: Jan Haller <bromeon@gmail.com>
2 parents 4bc1127 + 8da9fed commit 8c5cda8

File tree

8 files changed

+79
-148
lines changed

8 files changed

+79
-148
lines changed

examples/scene_create/src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,14 @@ fn update_panel(owner: &Spatial, num_children: i64) {
141141

142142
// Put the Node
143143
let mut as_variant = Variant::from_object(panel_node);
144-
match as_variant.call(
145-
"set_num_children",
146-
&[Variant::from_u64(num_children as u64)],
147-
) {
144+
let result = unsafe {
145+
as_variant.call(
146+
"set_num_children",
147+
&[Variant::from_u64(num_children as u64)],
148+
)
149+
};
150+
151+
match result {
148152
Ok(_) => godot_print!("Called Panel OK."),
149153
Err(_) => godot_print!("Error calling Panel"),
150154
}

gdnative-core/src/core_types/dictionary.rs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::iter::{Extend, FromIterator};
22
use std::marker::PhantomData;
33

4-
use gdnative_impl_proc_macros::doc_variant_collection_safety;
5-
64
use crate::core_types::GodotString;
75
use crate::private::get_api;
86
use crate::sys;
@@ -195,11 +193,6 @@ impl<Access: ThreadAccess> Dictionary<Access> {
195193
unsafe { VariantArray::<Unique>::from_sys((get_api().godot_dictionary_values)(self.sys())) }
196194
}
197195

198-
#[inline]
199-
pub fn get_next(&self, key: &Variant) -> &Variant {
200-
unsafe { Variant::cast_ref((get_api().godot_dictionary_next)(self.sys(), key.sys())) }
201-
}
202-
203196
/// Return a hashed i32 value representing the dictionary's contents.
204197
#[inline]
205198
pub fn hash(&self) -> i32 {
@@ -264,41 +257,6 @@ impl Dictionary<Shared> {
264257
pub fn new_shared() -> Self {
265258
Dictionary::<Unique>::new().into_shared()
266259
}
267-
268-
/// Inserts or updates the value of the element corresponding to the key.
269-
///
270-
#[doc_variant_collection_safety]
271-
#[inline]
272-
pub unsafe fn insert<K, V>(&self, key: K, val: V)
273-
where
274-
K: OwnedToVariant + ToVariantEq,
275-
V: OwnedToVariant,
276-
{
277-
(get_api().godot_dictionary_set)(
278-
self.sys_mut(),
279-
key.owned_to_variant().sys(),
280-
val.owned_to_variant().sys(),
281-
)
282-
}
283-
284-
/// Erase a key-value pair in the `Dictionary` by the specified key.
285-
///
286-
#[doc_variant_collection_safety]
287-
#[inline]
288-
pub unsafe fn erase<K>(&self, key: K)
289-
where
290-
K: ToVariant + ToVariantEq,
291-
{
292-
(get_api().godot_dictionary_erase)(self.sys_mut(), key.to_variant().sys())
293-
}
294-
295-
/// Clears the `Dictionary`, removing all key-value pairs.
296-
///
297-
#[doc_variant_collection_safety]
298-
#[inline]
299-
pub unsafe fn clear(&self) {
300-
(get_api().godot_dictionary_clear)(self.sys_mut())
301-
}
302260
}
303261

304262
/// Operations allowed on Dictionaries that may only be shared on the current thread.

gdnative-core/src/core_types/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Types that represent core data types of Godot.
1+
//! Types that represent [core types](https://docs.godotengine.org/en/stable/development/cpp/core_types.html) of Godot.
22
//!
33
//! In contrast to generated Godot class types from the `api` module, the types in here are hand-written in idiomatic Rust and
44
//! are the counterparts to built-in types in GDScript.

gdnative-core/src/core_types/variant.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -792,27 +792,31 @@ impl Variant {
792792
unsafe { (get_api().godot_variant_has_method)(&self.0, &method.0) }
793793
}
794794

795+
/// Invokes a method on the held object.
796+
///
797+
/// # Safety
798+
/// This method may invoke [Object::call()] internally, which is unsafe, as it allows
799+
/// execution of arbitrary code (including user-defined code in GDScript or unsafe Rust).
795800
#[inline]
796-
pub fn call(
801+
pub unsafe fn call(
797802
&mut self,
798803
method: impl Into<GodotString>,
799804
args: &[Variant],
800805
) -> Result<Variant, CallError> {
801806
let method = method.into();
802-
unsafe {
803-
let api = get_api();
804-
let mut err = sys::godot_variant_call_error::default();
805-
let mut arg_refs = args.iter().map(Variant::sys).collect::<Vec<_>>();
806-
let variant = (api.godot_variant_call)(
807-
&mut self.0,
808-
&method.0,
809-
arg_refs.as_mut_ptr(),
810-
args.len() as i32,
811-
&mut err,
812-
);
813807

814-
CallError::from_sys(err.error).map(|_| Variant::from_sys(variant))
815-
}
808+
let api = get_api();
809+
let mut err = sys::godot_variant_call_error::default();
810+
let mut arg_refs = args.iter().map(Variant::sys).collect::<Vec<_>>();
811+
let variant = (api.godot_variant_call)(
812+
&mut self.0,
813+
&method.0,
814+
arg_refs.as_mut_ptr(),
815+
args.len() as i32,
816+
&mut err,
817+
);
818+
819+
CallError::from_sys(err.error).map(|_| Variant::from_sys(variant))
816820
}
817821

818822
/// Evaluates a variant operator on `self` and `rhs` and returns the result on success.

gdnative-core/src/core_types/variant_array.rs

Lines changed: 25 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::iter::{Extend, FromIterator};
22
use std::marker::PhantomData;
33

4-
use gdnative_impl_proc_macros::doc_variant_collection_safety;
5-
64
use crate::private::get_api;
75
use crate::sys;
86

@@ -44,12 +42,14 @@ impl<Access: ThreadAccess> VariantArray<Access> {
4442
/// Sets the value of the element at the given offset.
4543
#[inline]
4644
pub fn set<T: OwnedToVariant>(&self, idx: i32, val: T) {
45+
self.check_bounds(idx);
4746
unsafe { (get_api().godot_array_set)(self.sys_mut(), idx, val.owned_to_variant().sys()) }
4847
}
4948

5049
/// Returns a copy of the element at the given offset.
5150
#[inline]
5251
pub fn get(&self, idx: i32) -> Variant {
52+
self.check_bounds(idx);
5353
unsafe { Variant((get_api().godot_array_get)(self.sys(), idx)) }
5454
}
5555

@@ -63,6 +63,7 @@ impl<Access: ThreadAccess> VariantArray<Access> {
6363
/// `Variant` is reference-counted and thus cheaply cloned. Consider using `get` instead.
6464
#[inline]
6565
pub unsafe fn get_ref(&self, idx: i32) -> &Variant {
66+
self.check_bounds(idx);
6667
Variant::cast_ref((get_api().godot_array_operator_index_const)(
6768
self.sys(),
6869
idx,
@@ -79,6 +80,7 @@ impl<Access: ThreadAccess> VariantArray<Access> {
7980
#[inline]
8081
#[allow(clippy::mut_from_ref)]
8182
pub unsafe fn get_mut_ref(&self, idx: i32) -> &mut Variant {
83+
self.check_bounds(idx);
8284
Variant::cast_mut_ref((get_api().godot_array_operator_index)(self.sys_mut(), idx))
8385
}
8486

@@ -220,6 +222,15 @@ impl<Access: ThreadAccess> VariantArray<Access> {
220222
std::mem::forget(self);
221223
VariantArray::from_sys(sys)
222224
}
225+
226+
fn check_bounds(&self, idx: i32) {
227+
assert!(
228+
idx >= 0 && idx < self.len(),
229+
"Index {} out of bounds (len {})",
230+
idx,
231+
self.len()
232+
);
233+
}
223234
}
224235

225236
/// Operations allowed on Dictionaries that can only be referenced to from the current thread.
@@ -335,78 +346,6 @@ impl VariantArray<Shared> {
335346
pub fn new_shared() -> Self {
336347
VariantArray::<Unique>::new().into_shared()
337348
}
338-
339-
/// Clears the array, resizing to 0.
340-
///
341-
#[doc_variant_collection_safety]
342-
#[inline]
343-
pub unsafe fn clear(&self) {
344-
(get_api().godot_array_clear)(self.sys_mut());
345-
}
346-
347-
/// Removes the element at `idx`.
348-
///
349-
#[doc_variant_collection_safety]
350-
#[inline]
351-
pub unsafe fn remove(&self, idx: i32) {
352-
(get_api().godot_array_remove)(self.sys_mut(), idx)
353-
}
354-
355-
/// Removed the first occurrence of `val`.
356-
///
357-
#[doc_variant_collection_safety]
358-
#[inline]
359-
pub unsafe fn erase<T: ToVariant>(&self, val: T) {
360-
(get_api().godot_array_erase)(self.sys_mut(), val.to_variant().sys())
361-
}
362-
363-
/// Resizes the array, filling with `Nil` if necessary.
364-
///
365-
#[doc_variant_collection_safety]
366-
#[inline]
367-
pub unsafe fn resize(&self, size: i32) {
368-
(get_api().godot_array_resize)(self.sys_mut(), size)
369-
}
370-
371-
/// Appends an element at the end of the array.
372-
///
373-
#[doc_variant_collection_safety]
374-
#[inline]
375-
pub unsafe fn push<T: OwnedToVariant>(&self, val: T) {
376-
(get_api().godot_array_push_back)(self.sys_mut(), val.owned_to_variant().sys());
377-
}
378-
379-
/// Removes an element at the end of the array.
380-
///
381-
#[doc_variant_collection_safety]
382-
#[inline]
383-
pub unsafe fn pop(&self) -> Variant {
384-
Variant((get_api().godot_array_pop_back)(self.sys_mut()))
385-
}
386-
387-
/// Appends an element to the front of the array.
388-
///
389-
#[doc_variant_collection_safety]
390-
#[inline]
391-
pub unsafe fn push_front<T: OwnedToVariant>(&self, val: T) {
392-
(get_api().godot_array_push_front)(self.sys_mut(), val.owned_to_variant().sys());
393-
}
394-
395-
/// Removes an element at the front of the array.
396-
///
397-
#[doc_variant_collection_safety]
398-
#[inline]
399-
pub unsafe fn pop_front(&self) -> Variant {
400-
Variant((get_api().godot_array_pop_front)(self.sys_mut()))
401-
}
402-
403-
/// Insert a new int at a given position in the array.
404-
///
405-
#[doc_variant_collection_safety]
406-
#[inline]
407-
pub unsafe fn insert<T: OwnedToVariant>(&self, at: i32, val: T) {
408-
(get_api().godot_array_insert)(self.sys_mut(), at, val.owned_to_variant().sys())
409-
}
410349
}
411350

412351
/// Operations allowed on Dictionaries that may only be shared on the current thread.
@@ -675,12 +614,24 @@ godot_test!(test_array {
675614

676615
godot_test!(
677616
test_array_debug {
617+
use std::panic::catch_unwind;
618+
678619
let arr = VariantArray::new(); // []
679620
arr.push(&Variant::from_str("hello world"));
680621
arr.push(&Variant::from_bool(true));
681622
arr.push(&Variant::from_i64(42));
682623

683624
assert_eq!(format!("{:?}", arr), "[GodotString(hello world), Bool(True), I64(42)]");
625+
626+
let set = catch_unwind(|| { arr.set(3, 7i64); });
627+
let get = catch_unwind(|| { arr.get(3); });
628+
let get_ref = catch_unwind(|| { unsafe { arr.get_ref(3) }; });
629+
let get_mut_ref = catch_unwind(|| { unsafe { arr.get_mut_ref(3) }; });
630+
631+
assert!(set.is_err(), "set() out of bounds causes panic");
632+
assert!(get.is_err(), "get() out of bounds causes panic");
633+
assert!(get_ref.is_err(), "get_mut() out of bounds causes panic");
634+
assert!(get_mut_ref.is_err(), "get_mut_ref() out of bounds causes panic");
684635
}
685636
);
686637

impl/proc_macros/src/doc.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
use proc_macro2::TokenStream;
2-
use quote::ToTokens;
31
use syn::visit_mut::VisitMut;
4-
use syn::{Attribute, Item, ItemFn, ItemImpl};
2+
use syn::{Attribute, ItemFn, ItemImpl};
3+
4+
/*
5+
Leaving code commented-out, as this might be very useful elsewhere
56
7+
use proc_macro2::TokenStream;
8+
use quote::ToTokens;
9+
use syn::{Item};
610
pub fn variant_collection_safety(
711
_attr: proc_macro::TokenStream,
812
item: proc_macro::TokenStream,
@@ -31,6 +35,7 @@ pub fn variant_collection_safety(
3135
visit.visit_item_mut(&mut item);
3236
Ok(item.to_token_stream())
3337
}
38+
*/
3439

3540
struct IncludeDocs<'a> {
3641
docs: &'a [&'a str],

impl/proc_macros/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@ pub fn decl_typed_array_element(input: TokenStream) -> TokenStream {
2424
.into()
2525
}
2626

27+
/*
28+
Leaving code commented-out, as this might be very useful elsewhere
29+
2730
#[proc_macro_attribute]
2831
pub fn doc_variant_collection_safety(attr: TokenStream, item: TokenStream) -> TokenStream {
2932
self::doc::variant_collection_safety(attr, item)
3033
.unwrap_or_else(to_compile_errors)
3134
.into()
3235
}
36+
*/
3337

3438
fn to_compile_errors(error: syn::Error) -> proc_macro2::TokenStream {
3539
let compile_error = error.to_compile_error();

test/src/test_variant_call_args.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,34 +57,33 @@ fn test_variant_call_args() -> bool {
5757

5858
let mut base = obj.into_base().into_shared().to_variant();
5959

60-
assert_eq!(Some(42), base.call("zero", &[]).unwrap().try_to_i64());
60+
assert_eq!(Some(42), call_i64(&mut base, "zero", &[]));
6161

6262
assert_eq!(
6363
Some(126),
64-
base.call("one", &[Variant::from_i64(3),])
65-
.unwrap()
66-
.try_to_i64()
64+
call_i64(&mut base, "one", &[Variant::from_i64(3)])
6765
);
6866

6967
assert_eq!(
7068
Some(-10),
71-
base.call("two", &[Variant::from_i64(-1), Variant::from_i64(32),])
72-
.unwrap()
73-
.try_to_i64()
69+
call_i64(
70+
&mut base,
71+
"two",
72+
&[Variant::from_i64(-1), Variant::from_i64(32)]
73+
)
7474
);
7575

7676
assert_eq!(
7777
Some(-52),
78-
base.call(
78+
call_i64(
79+
&mut base,
7980
"three",
8081
&[
8182
Variant::from_i64(-2),
8283
Variant::from_i64(4),
8384
Variant::from_i64(8),
8485
]
8586
)
86-
.unwrap()
87-
.try_to_i64()
8887
);
8988
})
9089
.is_ok();
@@ -95,3 +94,9 @@ fn test_variant_call_args() -> bool {
9594

9695
ok
9796
}
97+
98+
fn call_i64(variant: &mut Variant, method: &str, args: &[Variant]) -> Option<i64> {
99+
let result = unsafe { variant.call(method, args) };
100+
101+
result.unwrap().try_to_i64()
102+
}

0 commit comments

Comments
 (0)