From 967508c37e34d06e37499e3dfc4a77b513712c43 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 5 Nov 2026 15:13:00 +0000 Subject: [PATCH 1/2] Bounds-check vector::at in refcount --- rules/vector/tgt_refcount.rs | 6 +++++- tests/ub/vector_at_out_of_range.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/ub/vector_at_out_of_range.cpp diff --git a/rules/vector/tgt_refcount.rs b/rules/vector/tgt_refcount.rs index 5f48646b0d..558a6b6b10 100644 --- a/rules/vector/tgt_refcount.rs +++ b/rules/vector/tgt_refcount.rs @@ -40,7 +40,11 @@ fn f65(a0: Ptr) -> Ptr { } fn f66(a0: Ptr, a1: usize) -> Ptr { - a0.offset(a1 as isize) + if a1 as usize >= a0.len() { + panic!("out of bounds access") + } else { + a0.offset(a1 as isize) + } } fn f68(a0: Ptr) -> Ptr { diff --git a/tests/ub/vector_at_out_of_range.cpp b/tests/ub/vector_at_out_of_range.cpp new file mode 100644 index 0000000000..6524c38650 --- /dev/null +++ b/tests/ub/vector_at_out_of_range.cpp @@ -0,0 +1,9 @@ +// panic-ub +#include + +int main() { + std::vector v = {1, 2, 3}; + auto idx = v.size(); + int *p = &v.at(idx); + return p == nullptr; +} From 12551155ce0fe31fc806d3be627ea9430c1a6d16 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 5 Nov 2026 15:29:36 +0000 Subject: [PATCH 2/2] Update tests --- .../ub/out/refcount/vector_at_out_of_range.rs | 23 ++++ tests/ub/out/unsafe/vector_at_out_of_range.rs | 21 ++++ tests/unit/out/refcount/implicit_autoref.rs | 10 +- tests/unit/out/refcount/nested_vector.rs | 14 ++- tests/unit/out/refcount/vector.rs | 88 +++++++++++--- .../out/refcount/vector_with_allocator.rs | 108 +++++++++++++----- 6 files changed, 216 insertions(+), 48 deletions(-) create mode 100644 tests/ub/out/refcount/vector_at_out_of_range.rs create mode 100644 tests/ub/out/unsafe/vector_at_out_of_range.rs diff --git a/tests/ub/out/refcount/vector_at_out_of_range.rs b/tests/ub/out/refcount/vector_at_out_of_range.rs new file mode 100644 index 0000000000..b6fb6e8dd3 --- /dev/null +++ b/tests/ub/out/refcount/vector_at_out_of_range.rs @@ -0,0 +1,23 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let v: Value> = Rc::new(RefCell::new(Vec::from([1, 2, 3]))); + let mut idx: usize = (*v.borrow()).len(); + let mut p: Ptr = (if idx as usize >= v.decay().len() { + panic!("out of bounds access") + } else { + v.decay().offset(idx as isize) + }); + return (((p).is_null()) as i32); +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/ub/out/unsafe/vector_at_out_of_range.rs b/tests/ub/out/unsafe/vector_at_out_of_range.rs new file mode 100644 index 0000000000..0d1d04d641 --- /dev/null +++ b/tests/ub/out/unsafe/vector_at_out_of_range.rs @@ -0,0 +1,21 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut v: Vec = Vec::from([1, 2, 3]); + let mut idx: usize = v.len(); + let mut p: *mut i32 = (&mut (v)[idx as usize] as *mut i32); + return (((p).is_null()) as i32); +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/implicit_autoref.rs b/tests/unit/out/refcount/implicit_autoref.rs index 4546818fe4..34b58f55d2 100644 --- a/tests/unit/out/refcount/implicit_autoref.rs +++ b/tests/unit/out/refcount/implicit_autoref.rs @@ -56,7 +56,15 @@ fn main_0() -> i32 { assert!(((index!(p, 1_usize).read()) == 30)); assert!((b == 40)); assert!(((vec_elem!((hp), v, 1_usize).read()) == 60)); - ({ write_through_0((p.decay().offset(0_usize as isize))) }); + ({ + write_through_0( + (if 0_usize as usize >= p.decay().len() { + panic!("out of bounds access") + } else { + p.decay().offset(0_usize as isize) + }), + ) + }); assert!(((index!(p, 0_usize).read()) == 42)); return 0; } diff --git a/tests/unit/out/refcount/nested_vector.rs b/tests/unit/out/refcount/nested_vector.rs index ec172c638a..10fcfeaf36 100644 --- a/tests/unit/out/refcount/nested_vector.rs +++ b/tests/unit/out/refcount/nested_vector.rs @@ -17,7 +17,19 @@ fn main_0() -> i32 { let a0_clone = v2.clone(); (*v1.borrow_mut()).push(a0_clone) }; - assert!(((v1.decay().to_last().decay().offset(0_usize as isize).read()) == 1)); + assert!( + ((match (v1.decay().to_last(),) { + (__tmp_0,) => { + if 0_usize as usize >= __tmp_0.decay().len() { + panic!("out of bounds access") + } else { + __tmp_0.decay().offset(0_usize as isize) + } + } + } + .read()) + == 1) + ); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/vector.rs b/tests/unit/out/refcount/vector.rs index 3b52b3b4f1..16efd29293 100644 --- a/tests/unit/out/refcount/vector.rs +++ b/tests/unit/out/refcount/vector.rs @@ -282,39 +282,91 @@ fn main_0() -> i32 { x0 = 6_f64; assert!(((v6.decay().to_last().read()) == 5_f64)); let mut idx: i32 = 0; - assert!(((v6.decay().offset((idx as usize) as isize).read()) == 2_f64)); assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (idx as usize) },) { + (mut __tmp_11,) => { + if __tmp_11 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_11 as isize) + } + } + } + .read()) + == 2_f64) + ); + assert!( + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_12,) => { + if __tmp_12 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_12 as isize) + } + } + } + .read()) == 5_f64) ); - let ref1: Ptr = v6.decay().offset((s2).wrapping_sub(1_usize) as isize); + let ref1: Ptr = match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_13,) => { + if __tmp_13 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_13 as isize) + } + } + }; { ref1.with_mut(|__v| *__v = *__v + 1.5_f64) }; assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_14,) => { + if __tmp_14 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_14 as isize) + } + } + } + .read()) == 6.5_f64) ); - let mut x1: f64 = (v6 - .decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()); + let mut x1: f64 = (match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_15,) => { + if __tmp_15 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_15 as isize) + } + } + } + .read()); assert!((x1 == 6.5_f64)); x1 -= 1.5_f64; assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_16,) => { + if __tmp_16 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_16 as isize) + } + } + } + .read()) == 6.5_f64) ); assert!( - (((s1).wrapping_add(s2)) - .wrapping_add(((v2.decay().offset(0_usize as isize).read()) as usize)) - == 103_usize) + (((s1).wrapping_add(s2)).wrapping_add( + ((if 0_usize as usize >= v2.decay().len() { + panic!("out of bounds access") + } else { + v2.decay().offset(0_usize as isize) + } + .read()) as usize) + ) == 103_usize) ); let a: Value = Rc::new(RefCell::new(S { x: 1 })); let pa: Value> = Rc::new(RefCell::new((a.as_pointer()))); diff --git a/tests/unit/out/refcount/vector_with_allocator.rs b/tests/unit/out/refcount/vector_with_allocator.rs index 9f3f91356d..c903934771 100644 --- a/tests/unit/out/refcount/vector_with_allocator.rs +++ b/tests/unit/out/refcount/vector_with_allocator.rs @@ -184,33 +184,80 @@ fn main_0() -> i32 { x0 = 6_f64; assert!(((v6.decay().to_last().read()) == 5_f64)); let mut idx: i32 = 0; - assert!(((v6.decay().offset((idx as usize) as isize).read()) == 2_f64)); assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (idx as usize) },) { + (mut __tmp_2,) => { + if __tmp_2 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_2 as isize) + } + } + } + .read()) + == 2_f64) + ); + assert!( + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_3,) => { + if __tmp_3 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_3 as isize) + } + } + } + .read()) == 5_f64) ); - let ref1: Ptr = v6.decay().offset((s2).wrapping_sub(1_usize) as isize); + let ref1: Ptr = match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_4,) => { + if __tmp_4 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_4 as isize) + } + } + }; { ref1.with_mut(|__v| *__v = *__v + 1.5_f64) }; assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_5,) => { + if __tmp_5 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_5 as isize) + } + } + } + .read()) == 6.5_f64) ); - let mut x1: f64 = (v6 - .decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()); + let mut x1: f64 = (match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_6,) => { + if __tmp_6 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_6 as isize) + } + } + } + .read()); assert!((x1 == 6.5_f64)); x1 -= 1.5_f64; assert!( - ((v6.decay() - .offset((s2).wrapping_sub(1_usize) as isize) - .read()) + ((match ({ (s2).wrapping_sub(1_usize) },) { + (mut __tmp_7,) => { + if __tmp_7 as usize >= v6.decay().len() { + panic!("out of bounds access") + } else { + v6.decay().offset(__tmp_7 as isize) + } + } + } + .read()) == 6.5_f64) ); let v7: Value> = Rc::new(RefCell::new(Vec::new())); @@ -238,10 +285,10 @@ fn main_0() -> i32 { ({ fn_1(v7.as_pointer(), v8.clone()) }); let src: Value> = Rc::new(RefCell::new(Box::new([1_u32, 2_u32, 3_u32]))); let mut v9: Vec = match ({ (src.as_pointer() as Ptr).offset((3) as isize) },) { - (mut __tmp_2,) => { + (mut __tmp_8,) => { let mut __v = Vec::new(); let mut __it = (src.as_pointer() as Ptr); - while __it != __tmp_2 { + while __it != __tmp_8 { __v.push((__it.iter_deref().read())); __it.iter_inc(); } @@ -251,10 +298,10 @@ fn main_0() -> i32 { assert!((v9.len() == 3_usize)); assert!(((v9[0_usize] == 1_u32) && (v9[1_usize] == 2_u32)) && (v9[2_usize] == 3_u32)); let mut v10: Vec = match ({ (src.as_pointer() as Ptr).offset((3) as isize) },) { - (mut __tmp_3,) => { + (mut __tmp_9,) => { let mut __v = Vec::new(); let mut __it = (src.as_pointer() as Ptr); - while __it != __tmp_3 { + while __it != __tmp_9 { __v.push(((__it.iter_deref().read()) as u64)); __it.iter_inc(); } @@ -264,10 +311,10 @@ fn main_0() -> i32 { assert!((v10.len() == 3_usize)); assert!(((v10[0_usize] == 1_u64) && (v10[1_usize] == 2_u64)) && (v10[2_usize] == 3_u64)); let mut v11: Vec = match ({ (src.as_pointer() as Ptr).offset((3) as isize) },) { - (mut __tmp_4,) => { + (mut __tmp_10,) => { let mut __v = Vec::new(); let mut __it = (src.as_pointer() as Ptr); - while __it != __tmp_4 { + while __it != __tmp_10 { __v.push(((__it.iter_deref().read()) as i32)); __it.iter_inc(); } @@ -278,10 +325,10 @@ fn main_0() -> i32 { assert!(((v11[0_usize] == 1) && (v11[1_usize] == 2)) && (v11[2_usize] == 3)); let src1: Value> = Rc::new(RefCell::new(Box::new([1_u32, 2_u32, 3_u32]))); let mut v12: Vec = match ({ (src1.as_pointer() as Ptr).to_end() },) { - (mut __tmp_5,) => { + (mut __tmp_11,) => { let mut __v = Vec::new(); let mut __it = (src1.as_pointer() as Ptr); - while __it != __tmp_5 { + while __it != __tmp_11 { __v.push((__it.iter_deref().read())); __it.iter_inc(); } @@ -295,10 +342,10 @@ fn main_0() -> i32 { let mut start: Ptr = (buf.as_pointer() as Ptr); let mut len: usize = 5_usize; let mut v13: Vec = match ({ start.offset((len) as isize) },) { - (mut __tmp_6,) => { + (mut __tmp_12,) => { let mut __v = Vec::new(); let mut __it = (start).clone(); - while __it != __tmp_6 { + while __it != __tmp_12 { __v.push((__it.iter_deref().read())); __it.iter_inc(); } @@ -308,9 +355,14 @@ fn main_0() -> i32 { assert!((v13.len() == 5_usize)); assert!(((v13[0_usize] as i32) == 10) && ((v13[4_usize] as i32) == 50)); assert!( - (((s1).wrapping_add(s2)) - .wrapping_add(((v2.decay().offset(0_usize as isize).read()) as usize)) - == 103_usize) + (((s1).wrapping_add(s2)).wrapping_add( + ((if 0_usize as usize >= v2.decay().len() { + panic!("out of bounds access") + } else { + v2.decay().offset(0_usize as isize) + } + .read()) as usize) + ) == 103_usize) ); return 0; }