Skip to content

Commit 66c1e2c

Browse files
committed
Rust: Add test cases for implicit dereferences and more pointer/enum mixes (inspired by early real world results).
1 parent e2fb1d3 commit 66c1e2c

File tree

2 files changed

+115
-21
lines changed

2 files changed

+115
-21
lines changed

rust/ql/test/query-tests/security/CWE-825/lifetime.rs

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -257,41 +257,103 @@ pub fn test_loop() {
257257
}
258258
}
259259

260-
// --- enum ---
260+
// --- enums ---
261261

262262
enum MyEnum {
263263
Value(i64),
264264
}
265265

266-
impl Drop for MyEnum {
267-
fn drop(&mut self) {
268-
println!(" drop MyEnum");
269-
}
266+
enum MyEnum2 {
267+
Pointer(*const i64),
270268
}
271269

272-
pub fn test_enum() {
270+
pub fn get_pointer_to_enum() -> *const MyEnum {
271+
let e1 = MyEnum::Value(1);
272+
let result: *const MyEnum = &e1; // $ MISSING: Source[rust/access-after-lifetime-ended]=e1
273+
274+
result
275+
} // (e1 goes out of scope, so result is dangling)
276+
277+
pub fn get_pointer_in_enum() -> MyEnum2 {
278+
let v2 = 2;
279+
let e2 = MyEnum2::Pointer(&v2); // $ MISSING: Source[rust/access-after-lifetime-ended]=v2
280+
281+
e2
282+
} // (v2 goes out of scope, so the contained pointer is dangling)
283+
284+
pub fn get_pointer_from_enum() -> *const i64 {
285+
let e3 = MyEnum::Value(3);
273286
let result: *const i64;
274287

275-
{
276-
let e1 = MyEnum::Value(1);
288+
result = match e3 {
289+
MyEnum::Value(x) => { &x } // $ MISSING: Source[rust/access-after-lifetime-ended]=match_x
290+
}; // (x goes out of scope, so result is possibly dangling already)
277291

278-
result = match e1 {
279-
MyEnum::Value(x) => { &x }
280-
}; // (x goes out of scope, so result is dangling, I think; seen in real world code)
292+
use_the_stack();
281293

282-
use_the_stack();
294+
unsafe {
295+
let v0 = *result; // ?
296+
println!(" v0 = {v0} (?)");
297+
}
283298

284-
unsafe {
285-
let v1 = *result; // $ MISSING: Alert
286-
println!(" v1 = {v1}");
287-
}
288-
} // (e1 goes out of scope, so result is definitely dangling now)
299+
result
300+
} // (e3 goes out of scope, so result is definitely dangling now)
301+
302+
pub fn test_enums() {
303+
let e1 = get_pointer_to_enum();
304+
let e2 = get_pointer_in_enum();
305+
let result = get_pointer_from_enum();
289306

290307
use_the_stack();
291308

292309
unsafe {
293-
let v2 = *result; // $ MISSING: Alert
294-
println!(" v2 = {v2}"); // dropped in practice
310+
if let MyEnum::Value(v1) = *e1 { // $ MISSING: Alert[rust/access-after-lifetime-ended]=e1
311+
println!(" v1 = {v1} (!)"); // corrupt in practice
312+
}
313+
if let MyEnum2::Pointer(p2) = e2 {
314+
let v2 = unsafe { *p2 }; // $ MISSING: Alert[rust/access-after-lifetime-ended]=v2
315+
println!(" v2 = {v2} (!)"); // corrupt in practice
316+
}
317+
let v3 = *result; // $ MISSING: Alert[rust/access-after-lifetime-ended]=match_x
318+
println!(" v3 = {v3} (!)"); // corrupt in practice
319+
}
320+
}
321+
322+
// --- recursive enum ---
323+
324+
enum RecursiveEnum {
325+
Wrapper(Box<RecursiveEnum>),
326+
Pointer(*const i64),
327+
}
328+
329+
pub fn get_recursive_enum() -> Box<RecursiveEnum> {
330+
let v1 = 1;
331+
let enum1 = RecursiveEnum::Wrapper(Box::new(RecursiveEnum::Pointer(&v1))); // Source[rust/access-after-lifetime-ended]=v1
332+
let mut ref1 = &enum1;
333+
334+
while let RecursiveEnum::Wrapper(inner) = ref1 {
335+
println!(" wrapper");
336+
ref1 = &inner;
337+
}
338+
if let RecursiveEnum::Pointer(ptr) = ref1 {
339+
let v2: i64 = unsafe { **ptr }; // GOOD
340+
println!(" v2 = {v2}");
341+
}
342+
343+
return Box::new(enum1);
344+
} // (v1 goes out of scope, thus the contained pointer is dangling)
345+
346+
pub fn test_recursive_enums() {
347+
let enum1 = *get_recursive_enum();
348+
let mut ref1 = &enum1;
349+
350+
while let RecursiveEnum::Wrapper(inner) = ref1 {
351+
println!(" wrapper");
352+
ref1 = &inner;
353+
}
354+
if let RecursiveEnum::Pointer(ptr) = ref1 {
355+
let v3: i64 = unsafe { **ptr }; // Alert[rust/access-after-lifetime-ended]=v1
356+
println!(" v3 = {v3} (!)"); // corrupt in practice
295357
}
296358
}
297359

@@ -580,3 +642,29 @@ pub fn test_lifetime_annotations() {
580642
println!(" v4 = {v4} (!)"); // corrupt in practice
581643
}
582644
}
645+
646+
// --- implicit dereferences ---
647+
648+
pub fn test_implicit_derefs() {
649+
let ref1;
650+
{
651+
let str2;
652+
{
653+
let str1 = "bar";
654+
str2 = "foo".to_string() + &str1; // $ MISSING: Source[rust/access-after-lifetime-ended]=str1
655+
ref1 = &raw const str2; // $ MISSING: Source[rust/access-after-lifetime-ended]=str2
656+
} // (str1 goes out of scope, but it's been copied into str2)
657+
658+
unsafe {
659+
let v1 = &*ref1; // GOOD
660+
println!(" v1 = {v1}");
661+
}
662+
} // (str2 goes out of scope, thus ref1 is dangling)
663+
664+
use_the_stack();
665+
666+
unsafe {
667+
let v2 = &*ref1; // $ MISSING: Alert[rust/access-after-lifetime-ended]=str2
668+
println!(" v2 = {v2} (!)"); // corrupt in practice
669+
}
670+
}

rust/ql/test/query-tests/security/CWE-825/main.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,11 @@ fn main() {
154154
println!("test_loop:");
155155
test_loop();
156156

157-
println!("test_enum:");
158-
test_enum();
157+
println!("test_enums:");
158+
test_enums();
159+
160+
println!("test_recursive_enums:");
161+
test_recursive_enums();
159162

160163
println!("test_ptr_to_struct:");
161164
test_ptr_to_struct(mode);
@@ -174,4 +177,7 @@ fn main() {
174177

175178
println!("test_lifetime_annotations:");
176179
test_lifetime_annotations();
180+
181+
println!("test_implicit_derefs:");
182+
test_implicit_derefs();
177183
}

0 commit comments

Comments
 (0)