Skip to content

Commit f3ab4f9

Browse files
committed
Exploit Packed*Array index access returning nullptr on out-of-bounds
1 parent 9196f8a commit f3ab4f9

File tree

2 files changed

+33
-45
lines changed

2 files changed

+33
-45
lines changed

godot-core/src/builtin/packed_array.rs

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,10 @@ macro_rules! impl_packed_array {
189189
///
190190
/// If `index` is out of bounds.
191191
pub fn get(&self, index: usize) -> Option<$Element> {
192-
if index >= self.len() {
193-
return None;
194-
}
192+
let ptr = self.ptr_or_none(index)?;
195193

196-
// SAFETY: bounds check above.
197-
unsafe {
198-
let ptr = self.ptr_unchecked(index);
199-
Some((*ptr).clone())
200-
}
194+
// SAFETY: if index was out of bounds, `ptr` would be `None` and return early.
195+
unsafe { Some((*ptr).clone()) }
201196
}
202197

203198
/// Finds the index of an existing value in a sorted array using binary search.
@@ -278,10 +273,11 @@ macro_rules! impl_packed_array {
278273
/// the array's elements after the inserted element. The larger the array, the slower
279274
/// `insert` will be.
280275
pub fn insert(&mut self, index: usize, value: $Element) {
281-
let len = self.len();
282-
assert!(
283-
index <= len,
284-
"Array insertion index {index} is out of bounds: length is {len}");
276+
// Intentional > and not >=.
277+
if index > self.len() {
278+
self.panic_out_of_bounds(index);
279+
}
280+
285281
self.as_inner().insert(to_i64(index), Self::into_arg(value));
286282
}
287283

@@ -332,14 +328,12 @@ macro_rules! impl_packed_array {
332328
// Include specific functions in the code only if the Packed*Array provides the function.
333329
impl_specific_packed_array_functions!($PackedArray);
334330

335-
/// Asserts that the given index refers to an existing element.
336-
///
331+
337332
/// # Panics
338333
///
339-
/// If `index` is out of bounds.
340-
fn check_bounds(&self, index: usize) {
341-
let len = self.len();
342-
assert!(index < len, "Array index {index} is out of bounds: length is {len}");
334+
/// Always.
335+
fn panic_out_of_bounds(&self, index: usize) -> ! {
336+
panic!("Array index {index} is out of bounds: length is {}", self.len());
343337
}
344338

345339
/// Returns a pointer to the element at the given index.
@@ -348,28 +342,21 @@ macro_rules! impl_packed_array {
348342
///
349343
/// If `index` is out of bounds.
350344
fn ptr(&self, index: usize) -> *const $Element {
351-
self.check_bounds(index);
352-
353-
// SAFETY: We just checked that the index is not out of bounds.
354-
let ptr = unsafe {
355-
let item_ptr: *const $IndexRetType =
356-
(interface_fn!($operator_index_const))(self.sys(), to_i64(index));
357-
item_ptr as *const $Element
358-
};
359-
assert!(!ptr.is_null());
360-
ptr
345+
self.ptr_or_none(index).unwrap_or_else(|| self.panic_out_of_bounds(index))
361346
}
362347

363-
/// Returns a pointer to the element at the given index, or `None` if `index` is out of bounds.
364-
///
365-
/// # Safety
366-
/// Bound checking is responsibility of the caller.
367-
unsafe fn ptr_unchecked(&self, index: usize) -> *const $Element {
368-
let item_ptr: *const $IndexRetType = interface_fn!($operator_index_const)(self.sys(), to_i64(index));
369-
let ptr = item_ptr as *const $Element;
348+
/// Returns a pointer to the element at the given index, or `None` if out of bounds.
349+
fn ptr_or_none(&self, index: usize) -> Option<*const $Element> {
350+
// SAFETY: The packed array index operators return a null pointer on out-of-bounds.
351+
let item_ptr: *const $IndexRetType = unsafe {
352+
interface_fn!($operator_index_const)(self.sys(), to_i64(index))
353+
};
370354

371-
assert!(!ptr.is_null());
372-
ptr
355+
if item_ptr.is_null() {
356+
None
357+
} else {
358+
Some(item_ptr as *const $Element)
359+
}
373360
}
374361

375362
/// Returns a mutable pointer to the element at the given index.
@@ -378,16 +365,16 @@ macro_rules! impl_packed_array {
378365
///
379366
/// If `index` is out of bounds.
380367
fn ptr_mut(&mut self, index: usize) -> *mut $Element {
381-
self.check_bounds(index);
368+
// SAFETY: The packed array index operators return a null pointer on out-of-bounds.
369+
let item_ptr: *mut $IndexRetType = unsafe {
370+
interface_fn!($operator_index)(self.sys_mut(), to_i64(index))
371+
};
382372

383-
// SAFETY: We just checked that the index is not out of bounds.
384-
let ptr = unsafe {
385-
let item_ptr: *mut $IndexRetType =
386-
(interface_fn!($operator_index))(self.sys_mut(), to_i64(index));
373+
if item_ptr.is_null() {
374+
self.panic_out_of_bounds(index)
375+
} else {
387376
item_ptr as *mut $Element
388-
};
389-
assert!(!ptr.is_null());
390-
ptr
377+
}
391378
}
392379

393380
#[doc = concat!("Converts a `", stringify!($Element), "` into a value that can be")]

godot-ffi/src/toolbox.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub fn force_mut_ptr<T>(ptr: *const T) -> *mut T {
9797
pub fn to_const_ptr<T>(ptr: *mut T) -> *const T {
9898
ptr as *const T
9999
}
100+
100101
/// If `ptr` is not null, returns `Some(mapper(ptr))`; otherwise `None`.
101102
#[inline]
102103
pub fn ptr_then<T, R, F>(ptr: *mut T, mapper: F) -> Option<R>

0 commit comments

Comments
 (0)