Skip to content

Commit ae19ecc

Browse files
committed
Rust: Add test cases involving lifetimes + closures and async blocks.
1 parent 43cb98a commit ae19ecc

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,93 @@ pub fn test_rc() {
441441
// note: simialar things are likely possible with Ref, RefMut, RefCell,
442442
// Vec and others.
443443
}
444+
445+
// --- closures ---
446+
447+
fn get_closure(p3: *const i64, p4: *const i64) -> impl FnOnce() {
448+
let my_local1: i64 = 1;
449+
let my_local2: i64 = 2;
450+
let p1: *const i64 = &my_local1;
451+
452+
return move || { // captures `my_local2`, `p1`, `p3`, `p4` by value (due to `move`)
453+
let p2: *const i64 = &my_local2;
454+
455+
unsafe {
456+
let v1 = *p1; // $ MISSING: Alert
457+
let v2 = *p2; // GOOD
458+
let v3 = *p3; // GOOD
459+
let v4 = *p4; // $ MISSING: Alert
460+
println!(" v1 = {v1} (!)"); // corrupt in practice
461+
println!(" v2 = {v2}");
462+
println!(" v3 = {v3}");
463+
println!(" v4 = {v4} (!)");
464+
}
465+
};
466+
} // (`my_local1` goes out of scope, thus `p1` is dangling)
467+
468+
fn with_closure(ptr: *const i64, closure: fn(*const i64, *const i64)) {
469+
let my_local5: i64 = 5;
470+
471+
closure(ptr,
472+
&my_local5);
473+
}
474+
475+
pub fn test_closures() {
476+
let closure;
477+
let my_local3: i64 = 3;
478+
{
479+
let my_local4: i64 = 4;
480+
closure = get_closure( &my_local3,
481+
&my_local4);
482+
} // (`my_local4` goes out of scope, so `p4` is dangling)
483+
484+
use_the_stack();
485+
486+
closure();
487+
488+
with_closure(&my_local3, |p1, p2| {
489+
unsafe {
490+
let v5 = *p1; // GOOD
491+
let v6 = *p2; // GOOD
492+
println!(" v5 = {v5}");
493+
println!(" v6 = {v6}");
494+
}
495+
});
496+
}
497+
498+
// --- async ---
499+
500+
fn get_async_closure(p3: *const i64, p4: *const i64) -> impl std::future::Future<Output = ()> {
501+
let my_local1: i64 = 1;
502+
let my_local2: i64 = 2;
503+
let p1: *const i64 = &my_local1;
504+
505+
return async move { // captures `my_local2`, `p1`, `p3`, `p4` by value (due to `move`)
506+
let p2: *const i64 = &my_local2;
507+
508+
unsafe {
509+
let v1 = *p1; // $ MISSING: Alert
510+
let v2 = *p2; // GOOD
511+
let v3 = *p3; // GOOD
512+
let v4 = *p4; // $ MISSING: Alert
513+
println!(" v1 = {v1} (!)"); // corrupt in practice
514+
println!(" v2 = {v2}");
515+
println!(" v3 = {v3}");
516+
println!(" v4 = {v4} (!)");
517+
}
518+
};
519+
} // (`my_local1` goes out of scope, thus `p1` is dangling)
520+
521+
pub fn test_async() {
522+
let async_closure;
523+
let my_local3: i64 = 3;
524+
{
525+
let my_local4: i64 = 4;
526+
async_closure = get_async_closure(&my_local3,
527+
&my_local4);
528+
} // (`my_local4` goes out of scope, so `p4` is dangling)
529+
530+
use_the_stack();
531+
532+
futures::executor::block_on(async_closure);
533+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,10 @@ fn main() {
165165

166166
println!("test_rc:");
167167
test_rc();
168+
169+
println!("test_closures:");
170+
test_closures();
171+
172+
println!("test_async:");
173+
test_async();
168174
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
qltest_cargo_check: true
22
qltest_dependencies:
33
- libc = { version = "0.2.11" }
4+
- futures = { version = "0.3" }

0 commit comments

Comments
 (0)