Files
codeql/rust/ql/test/library-tests/dataflow/local/main.rs
2025-05-20 11:13:00 +01:00

584 lines
12 KiB
Rust

// Tests for intraprocedural data flow.
fn source(i: i64) -> i64 {
1000 + i
}
fn sink(s: i64) {
println!("{}", s);
}
fn sink_ref(sr: &i64) {
println!("{}", sr);
}
// -----------------------------------------------------------------------------
// Simple data flow through expressions and assignments
fn direct() {
sink(source(1)); // $ hasValueFlow=1
}
fn variable_usage() {
let s = source(2);
sink(s); // $ hasValueFlow=2
}
fn if_expression(cond: bool) {
let a = source(3);
let b = 2;
let c = if cond { a } else { b };
sink(c); // $ hasValueFlow=3
}
fn match_expression(m: Option<i64>) {
let a = source(4);
let b = match m {
Some(_) => a,
None => 0,
};
sink(b); // $ hasValueFlow=4
}
fn loop_with_break() {
let a = loop {
break 1;
};
sink(a);
let b = loop {
break source(5);
};
sink(b); // $ hasValueFlow=5
}
fn assignment() {
let mut i = 1;
sink(i);
i = source(6);
sink(i); // $ hasValueFlow=6
}
fn block_expression1() -> i64 {
let a = { 0 };
a
}
fn block_expression2(b: bool) -> i64 {
let a = 'block: {
if b {
break 'block 1;
};
2
};
a
}
fn block_expression3(b: bool) -> i64 {
let a = 'block: {
if b {
break 'block 1;
}
break 'block 2;
};
a
}
// -----------------------------------------------------------------------------
// Data flow through `Box`
fn box_deref() {
let i = Box::new(source(7));
sink(*i); // $ hasValueFlow=7
}
// -----------------------------------------------------------------------------
// Data flow through tuples
fn tuple() {
let a = (source(8), 2);
sink(a.0); // $ hasValueFlow=8
sink(a.1);
}
fn tuple_match() {
let a = (2, source(38), 2);
let (a0, a1, a2) = a;
sink(a0);
sink(a1); // $ hasValueFlow=38
sink(a2);
}
fn tuple_mutation() {
let mut a = (2, source(38));
sink(a.0);
sink(a.1); // $ hasValueFlow=38
a.0 = source(70);
a.1 = 2;
sink(a.0); // $ hasValueFlow=70
sink(a.1);
}
fn tuple_nested() {
let a = (3, source(59));
let b = (a, 3);
sink(b.0 .0);
sink(b.0 .1); // $ hasValueFlow=59
sink(b.1);
}
// -----------------------------------------------------------------------------
// Data flow through structs
struct Point {
x: i64,
y: i64,
}
fn struct_field() {
let p = Point { x: source(9), y: 2 };
sink(p.x); // $ hasValueFlow=9
sink(p.y);
}
fn struct_mutation() {
let mut p = Point { x: source(9), y: 2 };
sink(p.y);
p.y = source(54);
sink(p.y); // $ hasValueFlow=54
}
fn struct_pattern_match() {
let p = Point {
x: source(11),
y: 2,
};
let Point { x: a, y: b } = p;
sink(a); // $ hasValueFlow=11
sink(b);
}
struct Point3D {
plane: Point,
z: i64,
}
fn struct_nested_field() {
let p = Point3D {
plane: Point {
x: 2,
y: source(77),
},
z: 4,
};
sink(p.plane.x);
sink(p.plane.y); // $ hasValueFlow=77
sink(p.z);
}
fn struct_nested_match() {
let y = source(93);
let p = Point3D {
plane: Point { x: 2, y },
z: 4,
};
match p {
Point3D {
plane: Point { x, y },
z,
} => {
sink(x);
sink(y); // $ hasValueFlow=93
sink(z);
}
}
}
struct MyTupleStruct(i64, i64);
fn tuple_struct() {
let s = MyTupleStruct(source(94), 2);
sink(s.0); // $ hasValueFlow=94
sink(s.1);
match s {
MyTupleStruct(x, y) => {
sink(x); // $ hasValueFlow=94
sink(y);
}
}
}
// -----------------------------------------------------------------------------
// Data flow through enums
fn option_pattern_match_qualified() {
let s1 = Option::Some(source(13));
let s2 = Option::Some(2);
match s1 {
Option::Some(n) => sink(n), // $ hasValueFlow=13
Option::None => sink(0),
}
match s2 {
Option::Some(n) => sink(n),
Option::None => sink(0),
}
}
fn option_pattern_match_unqualified() {
let s1 = Some(source(14));
let s2 = Some(2);
match s1 {
Some(n) => sink(n), // $ hasValueFlow=14
None => sink(0),
}
match s2 {
Some(n) => sink(n),
None => sink(0),
}
}
fn option_unwrap() {
let s1 = Some(source(19));
sink(s1.unwrap()); // $ hasValueFlow=19
}
fn option_unwrap_or() {
let s1 = Some(source(46));
sink(s1.unwrap_or(0)); // $ hasValueFlow=46
let s2 = Some(0);
sink(s2.unwrap_or(source(47))); // $ hasValueFlow=47
}
fn option_unwrap_or_else() {
let s1 = Some(source(47));
sink(s1.unwrap_or_else(|| 0)); // $ hasValueFlow=47
let s2 = None;
sink(s2.unwrap_or_else(|| source(48))); // $ hasValueFlow=48
}
fn option_questionmark() -> Option<i64> {
let s1 = Some(source(20));
let s2 = Some(2);
let i1 = s1?;
sink(i1); // $ hasValueFlow=20
sink(s2?);
Some(0)
}
fn option_ok() {
let r1 : Result<i64, i64> = Ok(source(21));
let o1a : Option<i64> = r1.ok();
let o1b : Option<i64> = r1.err();
sink(o1a.unwrap()); // $ hasValueFlow=21
sink(o1b.unwrap());
let r2 : Result<i64, i64> = Err(source(22));
let o2a : Option<i64> = r2.ok();
let o2b : Option<i64> = r2.err();
sink(o2a.unwrap());
sink(o2b.unwrap()); // $ hasValueFlow=22
}
fn result_questionmark() -> Result<i64, i64> {
let s1: Result<i64, i64> = Ok(source(20));
let s2: Result<i64, i64> = Ok(2);
let s3: Result<i64, i64> = Err(source(77));
let i1 = s1?;
let i2 = s2?;
sink(i1); // $ hasValueFlow=20
sink(i2);
let i3 = s3?;
sink(i3); // No flow since value is in `Err`.
Ok(0)
}
fn result_expect() {
let s1: Result<i64, i64> = Ok(source(78));
sink(s1.expect("")); // $ hasValueFlow=78
sink(s1.expect_err(""));
let s2: Result<i64, i64> = Err(source(79));
sink(s2.expect(""));
sink(s2.expect_err("")); // $ hasValueFlow=79
}
enum MyTupleEnum {
A(i64),
B(i64),
}
fn custom_tuple_enum_pattern_match_qualified() {
let s1 = MyTupleEnum::A(source(15));
let s2 = MyTupleEnum::B(2);
match s1 {
MyTupleEnum::A(n) => sink(n), // $ hasValueFlow=15
MyTupleEnum::B(n) => sink(n),
}
match s1 {
MyTupleEnum::A(n) | MyTupleEnum::B(n) => sink(n), // $ hasValueFlow=15
}
match s2 {
MyTupleEnum::A(n) => sink(n),
MyTupleEnum::B(n) => sink(n),
}
}
use crate::MyTupleEnum::*;
fn custom_tuple_enum_pattern_match_unqualified() {
let s1 = A(source(16));
let s2 = B(2);
match s1 {
A(n) => sink(n), // $ hasValueFlow=16
B(n) => sink(n),
}
match s1 {
A(n) | B(n) => sink(n), // $ hasValueFlow=16
}
match s2 {
A(n) => sink(n),
B(n) => sink(n),
}
}
enum MyRecordEnum {
C { field_c: i64 },
D { field_d: i64 },
}
fn custom_record_enum_pattern_match_qualified() {
let s1 = MyRecordEnum::C {
field_c: source(17),
};
let s2 = MyRecordEnum::D { field_d: 2 };
match s1 {
MyRecordEnum::C { field_c: n } => sink(n), // $ hasValueFlow=17
MyRecordEnum::D { field_d: n } => sink(n),
}
match s1 {
MyRecordEnum::C { field_c: n } | MyRecordEnum::D { field_d: n } => sink(n), // $ hasValueFlow=17
}
match s2 {
MyRecordEnum::C { field_c: n } => sink(n),
MyRecordEnum::D { field_d: n } => sink(n),
}
}
use crate::MyRecordEnum::*;
fn custom_record_enum_pattern_match_unqualified() {
let s1 = C {
field_c: source(18),
};
let s2 = D { field_d: 2 };
match s1 {
C { field_c: n } => sink(n), // $ hasValueFlow=18
D { field_d: n } => sink(n),
}
match s1 {
C { field_c: n } | D { field_d: n } => sink(n), // $ hasValueFlow=18
}
match s2 {
C { field_c: n } => sink(n),
D { field_d: n } => sink(n),
}
}
// -----------------------------------------------------------------------------
// Data flow through arrays
fn array_lookup() {
let arr1 = [1, 2, source(94)];
let n1 = arr1[2];
sink(n1); // $ hasValueFlow=94
let arr2 = [source(20); 10];
let n2 = arr2[4];
sink(n2); // $ hasValueFlow=20
let arr3 = [1, 2, 3];
let n3 = arr3[2];
sink(n3);
}
fn array_for_loop() {
let arr1 = [1, 2, source(43)];
for n1 in arr1 {
sink(n1); // $ hasValueFlow=43
}
let arr2 = [1, 2, 3];
for n2 in arr2 {
sink(n2);
}
}
fn array_slice_pattern() {
let arr1 = [1, 2, source(43)];
match arr1 {
[a, b, c] => {
sink(a); // $ SPURIOUS: hasValueFlow=43
sink(b); // $ SPURIOUS: hasValueFlow=43
sink(c); // $ hasValueFlow=43
}
}
}
fn array_assignment() {
let mut mut_arr = [1, 2, 3];
sink(mut_arr[1]);
mut_arr[1] = source(55);
let d = mut_arr[1];
sink(d); // $ hasValueFlow=55
sink(mut_arr[0]); // $ SPURIOUS: hasValueFlow=55
}
// Test data flow inconsistency occuring with captured variables and `continue`
// in a loop.
pub fn captured_variable_and_continue(names: Vec<(bool, Option<String>)>) {
let default_name = source(83).to_string();
for (cond, name) in names {
if cond {
let n = name.unwrap_or_else(|| default_name.to_string());
sink(n.len() as i64);
continue;
}
}
}
macro_rules! get_source {
($e:expr) => {
source($e)
};
}
fn macro_invocation() {
let s = get_source!(37);
sink(s); // $ hasValueFlow=37
}
fn sink_string(s: String) {
println!("{}", s);
}
fn parse() {
let a = source(90);
let b = a.to_string();
let c = b.parse::<i64>().unwrap();
let d : i64 = b.parse().unwrap();
sink(a); // $ hasValueFlow=90
sink_string(b); // $ hasTaintFlow=90
sink(c); // $ hasTaintFlow=90
sink(d); // $ hasTaintFlow=90
}
fn iterators() {
let vs = [source(91), 2, 3, 4];
sink(vs[0]); // $ hasValueFlow=91
sink(*vs.iter().next().unwrap()); // $ MISSING: hasValueFlow=91
sink(*vs.iter().nth(0).unwrap()); // $ MISSING: hasValueFlow=91
for v in vs {
sink(v); // $ hasValueFlow=91
}
for &v in vs.iter() {
sink(v); // $ MISSING: hasValueFlow=91
}
let vs2 : Vec<&i64> = vs.iter().collect();
for &v in vs2 {
sink(v); // $ MISSING: hasValueFlow=91
}
vs.iter().map(|x| sink(*x)); // $ MISSING: hasValueFlow=91
vs.iter().for_each(|x| sink(*x)); // $ MISSING: hasValueFlow=91
for v in vs.into_iter() {
sink(v); // $ MISSING: hasValueFlow=91
}
let mut vs_mut = [source(92), 2, 3, 4];
sink(vs_mut[0]); // $ hasValueFlow=92
sink(*vs_mut.iter().next().unwrap()); // $ MISSING: hasValueFlow=92
sink(*vs_mut.iter().nth(0).unwrap()); // $ MISSING: hasValueFlow=92
for &mut v in vs_mut.iter_mut() {
sink(v); // $ MISSING: hasValueFlow=92
}
}
fn references() {
let a = source(40);
let b = source(41);
let c = source(42);
let c_ref = &c;
sink(a); // $ hasValueFlow=40
sink_ref(&b); // $ hasTaintFlow=41
sink_ref(c_ref); // $ hasTaintFlow=42
sink(*c_ref); // $ hasValueFlow=42
}
fn conversions() {
let a: i64 = source(50);
sink(a as i64); // $ hasTaintFlow=50
sink(a.into()); // $ MISSING: hasValueFlow=50
sink(i64::from(a)); // $ hasValueFlow=50
let b: i32 = source(51) as i32;
sink(b as i64); // $ hasTaintFlow=51
sink(b.into()); // $ MISSING: hasTaintFlow=51
sink(i64::from(b)); // $ hasTaintFlow=51
}
fn main() {
direct();
variable_usage();
if_expression(true);
match_expression(Some(0));
loop_with_break();
assignment();
box_deref();
tuple();
tuple_match();
tuple_mutation();
tuple_nested();
struct_field();
tuple_struct();
struct_mutation();
struct_pattern_match();
struct_nested_field();
struct_nested_match();
option_pattern_match_qualified();
option_pattern_match_unqualified();
option_unwrap();
option_unwrap_or();
option_questionmark();
option_ok();
let _ = result_questionmark();
custom_tuple_enum_pattern_match_qualified();
custom_tuple_enum_pattern_match_unqualified();
custom_record_enum_pattern_match_qualified();
custom_record_enum_pattern_match_unqualified();
block_expression1();
block_expression2(true);
block_expression3(true);
array_lookup();
array_for_loop();
array_slice_pattern();
array_assignment();
captured_variable_and_continue(vec![]);
macro_invocation();
parse();
iterators();
references();
conversions();
}