Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/Bultins.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* Provides classes for builtins.
*/

private import rust

/** The folder containing builtins. */
class BuiltinsFolder extends Folder {
BuiltinsFolder() {
this.getBaseName() = "builtins" and
this.getParentContainer().getBaseName() = "tools"
}
}

private class BuiltinsTypesFile extends File {
BuiltinsTypesFile() {
this.getBaseName() = "types.rs" and
this.getParentContainer() instanceof BuiltinsFolder
}
}

/**
* A builtin type, such as `bool` and `i32`.
*
* Builtin types are represented as structs.
*/
class BuiltinType extends Struct {
BuiltinType() { this.getFile() instanceof BuiltinsTypesFile }

/** Gets the name of this type. */
string getName() { result = super.getName().getText() }
}

/** The builtin `bool` type. */
class Bool extends BuiltinType {
Bool() { this.getName() = "bool" }
}

/** The builtin `char` type. */
class Char extends BuiltinType {
Char() { this.getName() = "char" }
}

/** The builtin `str` type. */
class Str extends BuiltinType {
Str() { this.getName() = "str" }
}

/** The builtin `i8` type. */
class I8 extends BuiltinType {
I8() { this.getName() = "i8" }
}

/** The builtin `i16` type. */
class I16 extends BuiltinType {
I16() { this.getName() = "i16" }
}

/** The builtin `i32` type. */
class I32 extends BuiltinType {
I32() { this.getName() = "i32" }
}

/** The builtin `i64` type. */
class I64 extends BuiltinType {
I64() { this.getName() = "i64" }
}

/** The builtin `i128` type. */
class I128 extends BuiltinType {
I128() { this.getName() = "i128" }
}

/** The builtin `u8` type. */
class U8 extends BuiltinType {
U8() { this.getName() = "u8" }
}

/** The builtin `u16` type. */
class U16 extends BuiltinType {
U16() { this.getName() = "u16" }
}

/** The builtin `u32` type. */
class U32 extends BuiltinType {
U32() { this.getName() = "u32" }
}

/** The builtin `u64` type. */
class U64 extends BuiltinType {
U64() { this.getName() = "u64" }
}

/** The builtin `u128` type. */
class U128 extends BuiltinType {
U128() { this.getName() = "u128" }
}

/** The builtin `usize` type. */
class USize extends BuiltinType {
USize() { this.getName() = "usize" }
}

/** The builtin `isize` type. */
class ISize extends BuiltinType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the Rust type is written as one word in all lower-case (and not something like iSize or i_size) I'd expect the the QL name to be Isize? Same for usize above.

ISize() { this.getName() = "isize" }
}

/** The builtin `f32` type. */
class F32 extends BuiltinType {
F32() { this.getName() = "f32" }
}

/** The builtin `f64` type. */
class F64 extends BuiltinType {
F64() { this.getName() = "f64" }
}
17 changes: 17 additions & 0 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ abstract class ItemNode extends Locatable {
or
preludeEdge(this, name, result) and not declares(this, _, name)
or
builtinEdge(this, name, result)
or
name = "super" and
if this instanceof Module or this instanceof SourceFile
then result = this.getImmediateParentModule()
Expand Down Expand Up @@ -1184,6 +1186,21 @@ private predicate preludeEdge(SourceFile f, string name, ItemNode i) {
)
}

private import codeql.rust.frameworks.stdlib.Bultins as Builtins

pragma[nomagic]
private predicate builtinEdge(ModuleLikeNode m, string name, ItemNode i) {
(
m instanceof SourceFile
or
m = any(CrateItemNode c).getModuleNode()
) and
exists(SourceFileItemNode builtins |
builtins.getFile().getParentContainer() instanceof Builtins::BuiltinsFolder and
i = builtins.getASuccessorRec(name)
)
}

/** Provides predicates for debugging the path resolution implementation. */
private module Debug {
private Locatable getRelevantLocatable() {
Expand Down
38 changes: 38 additions & 0 deletions rust/ql/lib/codeql/rust/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,41 @@ private Type inferTryExprType(TryExpr te, TypePath path) {
)
}

private import codeql.rust.frameworks.stdlib.Bultins as Builtins

pragma[nomagic]
StructType getBuiltinType(string name) {
result = TStruct(any(Builtins::BuiltinType t | name = t.getName()))
}

pragma[nomagic]
private StructType inferLiteralType(LiteralExpr le) {
le instanceof CharLiteralExpr and
result = TStruct(any(Builtins::Char t))
or
le instanceof StringLiteralExpr and
result = TStruct(any(Builtins::Str t))
or
le =
any(IntegerLiteralExpr n |
not exists(n.getSuffix()) and
result = getBuiltinType("i32")
or
result = getBuiltinType(n.getSuffix())
)
or
le =
any(FloatLiteralExpr n |
not exists(n.getSuffix()) and
result = getBuiltinType("f32")
or
result = getBuiltinType(n.getSuffix())
)
or
le instanceof BooleanLiteralExpr and
result = TStruct(any(Builtins::Bool t))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an idea for a simple refactor. It factors out the repeated TStruct and the repeated n.getSuffix() logic.

Note that this requires getSuffix to be an abstract predicate on NumberLiteralExpr which makes sense as all the extensions have that predicate.

Suggested change
pragma[nomagic]
StructType getBuiltinType(string name) {
result = TStruct(any(Builtins::BuiltinType t | name = t.getName()))
}
pragma[nomagic]
private StructType inferLiteralType(LiteralExpr le) {
le instanceof CharLiteralExpr and
result = TStruct(any(Builtins::Char t))
or
le instanceof StringLiteralExpr and
result = TStruct(any(Builtins::Str t))
or
le =
any(IntegerLiteralExpr n |
not exists(n.getSuffix()) and
result = getBuiltinType("i32")
or
result = getBuiltinType(n.getSuffix())
)
or
le =
any(FloatLiteralExpr n |
not exists(n.getSuffix()) and
result = getBuiltinType("f32")
or
result = getBuiltinType(n.getSuffix())
)
or
le instanceof BooleanLiteralExpr and
result = TStruct(any(Builtins::Bool t))
}
pragma[nomagic]
private StructType inferLiteralType(LiteralExpr le) {
exists(Builtins::BuiltinType t | result = TStruct(t) |
le instanceof CharLiteralExpr and
t instanceof Builtins::Char
or
le instanceof StringLiteralExpr and
t instanceof Builtins::Str
or
le =
any(NumberLiteralExpr ne |
t.getName() = ne.getSuffix()
or
not exists(ne.getSuffix()) and
(
ne instanceof IntegerLiteralExpr and
t instanceof Builtins::I32
or
ne instanceof FloatLiteralExpr and
t instanceof Builtins::F64
)
)
or
le instanceof BooleanLiteralExpr and
t instanceof Builtins::Bool
)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea.


cached
private module Cached {
private import codeql.rust.internal.CachedStages
Expand Down Expand Up @@ -1026,6 +1061,9 @@ private module Cached {
result = inferRefExprType(n, path)
or
result = inferTryExprType(n, path)
or
result = inferLiteralType(n) and
path.isEmpty()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
multiplePathResolutions
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:532:10:532:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
| main.rs:538:10:538:18 | ...::from | file://:0:0:0:0 | fn from |
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ models
| 2 | Summary: lang:core; <crate::option::Option>::unwrap; Argument[self].Field[crate::option::Option::Some(0)]; ReturnValue; value |
| 3 | Summary: lang:core; <crate::option::Option>::zip; Argument[0].Field[crate::option::Option::Some(0)]; ReturnValue.Field[crate::option::Option::Some(0)].Field[1]; value |
| 4 | Summary: lang:core; <crate::result::Result>::unwrap; Argument[self].Field[crate::result::Result::Ok(0)]; ReturnValue; value |
| 5 | Summary: lang:core; crate::ptr::read; Argument[0].Reference; ReturnValue; value |
| 6 | Summary: lang:core; crate::ptr::write; Argument[1]; Argument[0].Reference; value |
| 5 | Summary: lang:core; <i64 as crate::clone::Clone>::clone; Argument[self].Reference; ReturnValue; value |
| 6 | Summary: lang:core; crate::ptr::read; Argument[0].Reference; ReturnValue; value |
| 7 | Summary: lang:core; crate::ptr::write; Argument[1]; Argument[0].Reference; value |
edges
| main.rs:12:9:12:9 | a [Some] | main.rs:13:10:13:19 | a.unwrap() | provenance | MaD:2 |
| main.rs:12:9:12:9 | a [Some] | main.rs:14:13:14:13 | a [Some] | provenance | |
Expand All @@ -22,7 +23,12 @@ edges
| main.rs:21:13:21:13 | a [Ok] | main.rs:21:13:21:21 | a.clone() [Ok] | provenance | generated |
| main.rs:21:13:21:21 | a.clone() [Ok] | main.rs:21:9:21:9 | b [Ok] | provenance | |
| main.rs:26:9:26:9 | a | main.rs:27:10:27:10 | a | provenance | |
| main.rs:26:9:26:9 | a | main.rs:28:13:28:13 | a | provenance | |
| main.rs:26:13:26:22 | source(...) | main.rs:26:9:26:9 | a | provenance | |
| main.rs:28:9:28:9 | b | main.rs:29:10:29:10 | b | provenance | |
| main.rs:28:13:28:13 | a | main.rs:28:13:28:21 | a.clone() | provenance | MaD:5 |
| main.rs:28:13:28:13 | a | main.rs:28:13:28:21 | a.clone() | provenance | generated |
| main.rs:28:13:28:21 | a.clone() | main.rs:28:9:28:9 | b | provenance | |
| main.rs:41:13:41:13 | w [Wrapper] | main.rs:42:15:42:15 | w [Wrapper] | provenance | |
| main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | main.rs:41:13:41:13 | w [Wrapper] | provenance | |
| main.rs:41:30:41:39 | source(...) | main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | provenance | |
Expand All @@ -47,8 +53,8 @@ edges
| main.rs:61:18:61:23 | TuplePat [tuple.1] | main.rs:61:22:61:22 | m | provenance | |
| main.rs:61:22:61:22 | m | main.rs:63:22:63:22 | m | provenance | |
| main.rs:84:29:84:29 | [post] y [&ref] | main.rs:85:33:85:33 | y [&ref] | provenance | |
| main.rs:84:32:84:41 | source(...) | main.rs:84:29:84:29 | [post] y [&ref] | provenance | MaD:6 |
| main.rs:85:33:85:33 | y [&ref] | main.rs:85:18:85:34 | ...::read(...) | provenance | MaD:5 |
| main.rs:84:32:84:41 | source(...) | main.rs:84:29:84:29 | [post] y [&ref] | provenance | MaD:7 |
| main.rs:85:33:85:33 | y [&ref] | main.rs:85:18:85:34 | ...::read(...) | provenance | MaD:6 |
nodes
| main.rs:12:9:12:9 | a [Some] | semmle.label | a [Some] |
| main.rs:12:13:12:28 | Some(...) [Some] | semmle.label | Some(...) [Some] |
Expand All @@ -69,6 +75,10 @@ nodes
| main.rs:26:9:26:9 | a | semmle.label | a |
| main.rs:26:13:26:22 | source(...) | semmle.label | source(...) |
| main.rs:27:10:27:10 | a | semmle.label | a |
| main.rs:28:9:28:9 | b | semmle.label | b |
| main.rs:28:13:28:13 | a | semmle.label | a |
| main.rs:28:13:28:21 | a.clone() | semmle.label | a.clone() |
| main.rs:29:10:29:10 | b | semmle.label | b |
| main.rs:41:13:41:13 | w [Wrapper] | semmle.label | w [Wrapper] |
| main.rs:41:17:41:41 | Wrapper {...} [Wrapper] | semmle.label | Wrapper {...} [Wrapper] |
| main.rs:41:30:41:39 | source(...) | semmle.label | source(...) |
Expand Down Expand Up @@ -106,6 +116,7 @@ testFailures
| main.rs:20:10:20:19 | a.unwrap() | main.rs:19:34:19:43 | source(...) | main.rs:20:10:20:19 | a.unwrap() | $@ | main.rs:19:34:19:43 | source(...) | source(...) |
| main.rs:22:10:22:19 | b.unwrap() | main.rs:19:34:19:43 | source(...) | main.rs:22:10:22:19 | b.unwrap() | $@ | main.rs:19:34:19:43 | source(...) | source(...) |
| main.rs:27:10:27:10 | a | main.rs:26:13:26:22 | source(...) | main.rs:27:10:27:10 | a | $@ | main.rs:26:13:26:22 | source(...) | source(...) |
| main.rs:29:10:29:10 | b | main.rs:26:13:26:22 | source(...) | main.rs:29:10:29:10 | b | $@ | main.rs:26:13:26:22 | source(...) | source(...) |
| main.rs:43:38:43:38 | n | main.rs:41:30:41:39 | source(...) | main.rs:43:38:43:38 | n | $@ | main.rs:41:30:41:39 | source(...) | source(...) |
| main.rs:47:38:47:38 | n | main.rs:41:30:41:39 | source(...) | main.rs:47:38:47:38 | n | $@ | main.rs:41:30:41:39 | source(...) | source(...) |
| main.rs:63:22:63:22 | m | main.rs:58:22:58:31 | source(...) | main.rs:63:22:63:22 | m | $@ | main.rs:58:22:58:31 | source(...) | source(...) |
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/test/library-tests/dataflow/modeled/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn i64_clone() {
let a = source(12);
sink(a); // $ hasValueFlow=12
let b = a.clone();
sink(b); // $ MISSING: hasValueFlow=12 - lack of builtins means that we cannot resolve clone call above, and hence not insert implicit borrow
sink(b); // $ hasValueFlow=12
}

mod my_clone {
Expand Down
12 changes: 8 additions & 4 deletions rust/ql/test/library-tests/path-resolution/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn i() {

{
struct Foo {
x: i32,
x: i32, // $ item=i32
} // I30

let _ = Foo { x: 0 }; // $ item=I30
Expand Down Expand Up @@ -121,9 +121,13 @@ mod m6 {

mod m7 {
pub enum MyEnum {
A(i32), // I42
B { x: i32 }, // I43
C, // I44
A(
i32, // $ item=i32
), // I42
B {
x: i32, // $ item=i32
}, // I43
C, // I44
} // I41

#[rustfmt::skip]
Expand Down
6 changes: 3 additions & 3 deletions rust/ql/test/library-tests/path-resolution/my.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ type Result<
>; // my::Result

fn int_div(
x: i32, //
y: i32,
) -> Result<i32> // $ item=my::Result
x: i32, // $ item=i32
y: i32, // $ item=i32
) -> Result<i32> // $ item=my::Result $ item=i32
{
if y == 0 {
return Err("Div by zero".to_string());
Expand Down
Loading