mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20950 from paldepind/rust/ti-raw-pointer
Rust: Type inference for raw pointers
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved type inference for raw pointers (`*const` and `*mut`). This includes type inference for the raw borrow operators (`&raw const` and `&raw mut`) and dereferencing of raw pointers.
|
||||
@@ -176,15 +176,20 @@ class RefMutType extends BuiltinType {
|
||||
override string getDisplayName() { result = "&mut" }
|
||||
}
|
||||
|
||||
/** The builtin pointer type `*const T`. */
|
||||
class PtrType extends BuiltinType {
|
||||
PtrType() { this.getName() = "Ptr" }
|
||||
/** A builtin raw pointer type `*const T` or `*mut T`. */
|
||||
abstract private class PtrTypeImpl extends BuiltinType { }
|
||||
|
||||
final class PtrType = PtrTypeImpl;
|
||||
|
||||
/** The builtin raw pointer type `*const T`. */
|
||||
class PtrConstType extends PtrTypeImpl {
|
||||
PtrConstType() { this.getName() = "PtrConst" }
|
||||
|
||||
override string getDisplayName() { result = "*const" }
|
||||
}
|
||||
|
||||
/** The builtin pointer type `*mut T`. */
|
||||
class PtrMutType extends BuiltinType {
|
||||
/** The builtin raw pointer type `*mut T`. */
|
||||
class PtrMutType extends PtrTypeImpl {
|
||||
PtrMutType() { this.getName() = "PtrMut" }
|
||||
|
||||
override string getDisplayName() { result = "*mut" }
|
||||
|
||||
@@ -774,8 +774,11 @@ private TypeItemNode resolveBuiltin(TypeRepr tr) {
|
||||
tr instanceof RefTypeRepr and
|
||||
result instanceof Builtins::RefType
|
||||
or
|
||||
tr instanceof PtrTypeRepr and
|
||||
result instanceof Builtins::PtrType
|
||||
tr.(PtrTypeRepr).isConst() and
|
||||
result instanceof Builtins::PtrConstType
|
||||
or
|
||||
tr.(PtrTypeRepr).isMut() and
|
||||
result instanceof Builtins::PtrMutType
|
||||
or
|
||||
result.(Builtins::TupleType).getArity() = tr.(TupleTypeRepr).getNumberOfFields()
|
||||
}
|
||||
|
||||
@@ -339,12 +339,23 @@ class NeverType extends Type, TNeverType {
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
class PtrType extends StructType {
|
||||
PtrType() { this.getStruct() instanceof Builtins::PtrType }
|
||||
abstract class PtrType extends StructType { }
|
||||
|
||||
override string toString() { result = "*" }
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getPtrTypeParameter() {
|
||||
result = any(PtrType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
class PtrMutType extends PtrType {
|
||||
PtrMutType() { this.getStruct() instanceof Builtins::PtrMutType }
|
||||
|
||||
override string toString() { result = "*mut" }
|
||||
}
|
||||
|
||||
class PtrConstType extends PtrType {
|
||||
PtrConstType() { this.getStruct() instanceof Builtins::PtrConstType }
|
||||
|
||||
override string toString() { result = "*const" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,11 +388,6 @@ class UnknownType extends Type, TUnknownType {
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
TypeParamTypeParameter getPtrTypeParameter() {
|
||||
result = any(PtrType t).getPositionalTypeParameter(0)
|
||||
}
|
||||
|
||||
/** A type parameter. */
|
||||
abstract class TypeParameter extends Type {
|
||||
override TypeParameter getPositionalTypeParameter(int i) { none() }
|
||||
|
||||
@@ -424,7 +424,10 @@ module CertainTypeInference {
|
||||
or
|
||||
result = inferLiteralType(n, path, true)
|
||||
or
|
||||
result = inferRefNodeType(n) and
|
||||
result = inferRefPatType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferRefExprType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferLogicalOperationType(n, path)
|
||||
@@ -599,10 +602,14 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
|
||||
strictcount(Expr e | bodyReturns(n1, e)) = 1
|
||||
)
|
||||
or
|
||||
(
|
||||
n1 = n2.(RefExpr).getExpr() or
|
||||
n1 = n2.(RefPat).getPat()
|
||||
) and
|
||||
n2 =
|
||||
any(RefExpr re |
|
||||
n1 = re.getExpr() and
|
||||
prefix1.isEmpty() and
|
||||
prefix2 = TypePath::singleton(inferRefExprType(re).getPositionalTypeParameter(0))
|
||||
)
|
||||
or
|
||||
n1 = n2.(RefPat).getPat() and
|
||||
prefix1.isEmpty() and
|
||||
prefix2 = TypePath::singleton(getRefTypeParameter())
|
||||
or
|
||||
@@ -702,9 +709,7 @@ private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) {
|
||||
* of `n2` at `prefix2`, but type information should only propagate from `n1` to
|
||||
* `n2`.
|
||||
*/
|
||||
private predicate typeEqualityNonSymmetric(
|
||||
AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2
|
||||
) {
|
||||
private predicate typeEqualityAsymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) {
|
||||
lubCoercion(n2, n1, prefix2) and
|
||||
prefix1.isEmpty()
|
||||
or
|
||||
@@ -716,6 +721,13 @@ private predicate typeEqualityNonSymmetric(
|
||||
not lubCoercion(mid, n1, _) and
|
||||
prefix1 = prefixMid.append(suffix)
|
||||
)
|
||||
or
|
||||
// When `n2` is `*n1` propagate type information from a raw pointer type
|
||||
// parameter at `n1`. The other direction is handled in
|
||||
// `inferDereferencedExprPtrType`.
|
||||
n1 = n2.(DerefExpr).getExpr() and
|
||||
prefix1 = TypePath::singleton(getPtrTypeParameter()) and
|
||||
prefix2.isEmpty()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -728,7 +740,7 @@ private Type inferTypeEquality(AstNode n, TypePath path) {
|
||||
or
|
||||
typeEquality(n2, prefix2, n, prefix1)
|
||||
or
|
||||
typeEqualityNonSymmetric(n2, prefix2, n, prefix1)
|
||||
typeEqualityAsymmetric(n2, prefix2, n, prefix1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2999,16 +3011,21 @@ private Type inferFieldExprType(AstNode n, TypePath path) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the root type of the reference expression `ref`. */
|
||||
pragma[nomagic]
|
||||
private Type inferRefExprType(RefExpr ref) {
|
||||
if ref.isRaw()
|
||||
then
|
||||
ref.isMut() and result instanceof PtrMutType
|
||||
or
|
||||
ref.isConst() and result instanceof PtrConstType
|
||||
else result instanceof RefType
|
||||
}
|
||||
|
||||
/** Gets the root type of the reference node `ref`. */
|
||||
pragma[nomagic]
|
||||
private Type inferRefNodeType(AstNode ref) {
|
||||
(
|
||||
ref = any(IdentPat ip | ip.isRef()).getName()
|
||||
or
|
||||
ref instanceof RefExpr
|
||||
or
|
||||
ref instanceof RefPat
|
||||
) and
|
||||
private Type inferRefPatType(AstNode ref) {
|
||||
(ref = any(IdentPat ip | ip.isRef()).getName() or ref instanceof RefPat) and
|
||||
result instanceof RefType
|
||||
}
|
||||
|
||||
@@ -3192,6 +3209,27 @@ private Type inferIndexExprType(IndexExpr ie, TypePath path) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type getInferredDerefType(DerefExpr de, TypePath path) { result = inferType(de, path) }
|
||||
|
||||
pragma[nomagic]
|
||||
private PtrType getInferredDerefExprPtrType(DerefExpr de) { result = inferType(de.getExpr()) }
|
||||
|
||||
/**
|
||||
* Gets the inferred type of `n` at `path` when `n` occurs in a dereference
|
||||
* expression `*n` and when `n` is known to have a raw pointer type.
|
||||
*
|
||||
* The other direction is handled in `typeEqualityAsymmetric`.
|
||||
*/
|
||||
private Type inferDereferencedExprPtrType(AstNode n, TypePath path) {
|
||||
exists(DerefExpr de, PtrType type, TypePath suffix |
|
||||
de.getExpr() = n and
|
||||
type = getInferredDerefExprPtrType(de) and
|
||||
result = getInferredDerefType(de, suffix) and
|
||||
path = TypePath::cons(type.getPositionalTypeParameter(0), suffix)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A matching configuration for resolving types of struct patterns
|
||||
* like `let Foo { bar } = ...`.
|
||||
@@ -3593,6 +3631,8 @@ private module Cached {
|
||||
or
|
||||
result = inferIndexExprType(n, path)
|
||||
or
|
||||
result = inferDereferencedExprPtrType(n, path)
|
||||
or
|
||||
result = inferForLoopExprType(n, path)
|
||||
or
|
||||
result = inferDynamicCallExprType(n, path)
|
||||
|
||||
@@ -556,13 +556,18 @@ class NeverTypeReprMention extends TypeMention, NeverTypeRepr {
|
||||
}
|
||||
|
||||
class PtrTypeReprMention extends TypeMention instanceof PtrTypeRepr {
|
||||
private PtrType resolveRootType() {
|
||||
super.isConst() and result instanceof PtrConstType
|
||||
or
|
||||
super.isMut() and result instanceof PtrMutType
|
||||
}
|
||||
|
||||
override Type resolveTypeAt(TypePath path) {
|
||||
path.isEmpty() and
|
||||
result instanceof PtrType
|
||||
path.isEmpty() and result = this.resolveRootType()
|
||||
or
|
||||
exists(TypePath suffix |
|
||||
result = super.getTypeRepr().(TypeMention).resolveTypeAt(suffix) and
|
||||
path = TypePath::cons(getPtrTypeParameter(), suffix)
|
||||
path = TypePath::cons(this.resolveRootType().getPositionalTypeParameter(0), suffix)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| struct Array | |
|
||||
| struct Ptr | |
|
||||
| struct PtrConst | |
|
||||
| struct PtrMut | |
|
||||
| struct Ref | |
|
||||
| struct RefMut | |
|
||||
|
||||
60
rust/ql/test/library-tests/type-inference/raw_pointer.rs
Normal file
60
rust/ql/test/library-tests/type-inference/raw_pointer.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use std::ptr::null_mut;
|
||||
|
||||
fn raw_pointer_const_deref(x: *const i32) -> i32 {
|
||||
let _y = unsafe { *x }; // $ type=_y:i32
|
||||
0
|
||||
}
|
||||
|
||||
fn raw_pointer_mut_deref(x: *mut bool) -> i32 {
|
||||
let _y = unsafe { *x }; // $ type=_y:bool
|
||||
0
|
||||
}
|
||||
|
||||
fn raw_const_borrow() {
|
||||
let a: i64 = 10;
|
||||
let x = &raw const a; // $ type=x:TPtrConst.i64
|
||||
unsafe {
|
||||
let _y = *x; // $ type=_y:i64
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_mut_borrow() {
|
||||
let mut a = 10i32;
|
||||
let x = &raw mut a; // $ type=x:TPtrMut.i32
|
||||
unsafe {
|
||||
let _y = *x; // $ type=_y:i32
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_mut_write(cond: bool) {
|
||||
let a = 10i32;
|
||||
// The type of `ptr_written` must be inferred from the write below.
|
||||
let ptr_written = null_mut(); // $ target=null_mut type=ptr_written:TPtrMut.i32
|
||||
if cond {
|
||||
unsafe {
|
||||
// NOTE: This write is undefined behavior because `ptr_written` is a null pointer.
|
||||
*ptr_written = a;
|
||||
let _y = *ptr_written; // $ type=_y:i32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_type_from_deref(cond: bool) {
|
||||
// The type of `ptr_read` must be inferred from the read below.
|
||||
let ptr_read = null_mut(); // $ target=null_mut type=ptr_read:TPtrMut.i64
|
||||
if cond {
|
||||
unsafe {
|
||||
// NOTE: This read is undefined behavior because `ptr_read` is a null pointer.
|
||||
let _y: i64 = *ptr_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test() {
|
||||
raw_pointer_const_deref(&10); // $ target=raw_pointer_const_deref
|
||||
raw_pointer_mut_deref(&mut true); // $ target=raw_pointer_mut_deref
|
||||
raw_const_borrow(); // $ target=raw_const_borrow
|
||||
raw_mut_borrow(); // $ target=raw_mut_borrow
|
||||
raw_mut_write(false); // $ target=raw_mut_write
|
||||
raw_type_from_deref(false); // $ target=raw_type_from_deref
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,15 +4,25 @@ import codeql.rust.internal.Type
|
||||
import codeql.rust.internal.TypeInference as TypeInference
|
||||
import TypeInference
|
||||
|
||||
query predicate inferType(AstNode n, TypePath path, Type t) {
|
||||
t = TypeInference::inferType(n, path) and
|
||||
t != TUnknownType() and
|
||||
private predicate relevantNode(AstNode n) {
|
||||
n.fromSource() and
|
||||
not n.isFromMacroExpansion() and
|
||||
not n instanceof IdentPat and // avoid overlap in the output with the underlying `Name` node
|
||||
not n instanceof LiteralPat // avoid overlap in the output with the underlying `Literal` node
|
||||
}
|
||||
|
||||
query predicate inferCertainType(AstNode n, TypePath path, Type t) {
|
||||
t = TypeInference::CertainTypeInference::inferCertainType(n, path) and
|
||||
t != TUnknownType() and
|
||||
relevantNode(n)
|
||||
}
|
||||
|
||||
query predicate inferType(AstNode n, TypePath path, Type t) {
|
||||
t = TypeInference::inferType(n, path) and
|
||||
t != TUnknownType() and
|
||||
relevantNode(n)
|
||||
}
|
||||
|
||||
module ResolveTest implements TestSig {
|
||||
string getARelevantTag() { result = ["target", "fieldof"] }
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
multipleCallTargets
|
||||
| test.rs:117:9:117:21 | ptr.is_null() |
|
||||
@@ -1,7 +0,0 @@
|
||||
multipleCallTargets
|
||||
| main.rs:242:44:242:78 | ... .cast() |
|
||||
| main.rs:245:44:245:78 | ... .cast() |
|
||||
| main.rs:248:44:248:78 | ... .cast() |
|
||||
| main.rs:251:14:251:48 | ... .cast() |
|
||||
| main.rs:254:14:254:48 | ... .cast() |
|
||||
| main.rs:257:14:257:48 | ... .cast() |
|
||||
@@ -1,12 +1,4 @@
|
||||
multipleCallTargets
|
||||
| deallocation.rs:162:5:162:17 | ptr.is_null() |
|
||||
| deallocation.rs:174:7:174:19 | ptr.is_null() |
|
||||
| deallocation.rs:186:5:186:17 | ptr.is_null() |
|
||||
| deallocation.rs:190:5:190:17 | ptr.is_null() |
|
||||
| deallocation.rs:194:6:194:18 | ptr.is_null() |
|
||||
| deallocation.rs:202:5:202:17 | ptr.is_null() |
|
||||
| deallocation.rs:210:25:210:37 | ptr.is_null() |
|
||||
| deallocation.rs:225:5:225:23 | const_ptr.is_null() |
|
||||
| deallocation.rs:354:11:354:29 | ...::from(...) |
|
||||
| deallocation.rs:355:11:355:29 | ...::from(...) |
|
||||
| lifetime.rs:610:13:610:31 | ...::from(...) |
|
||||
|
||||
@@ -28,7 +28,7 @@ struct Slice<TSlice>;
|
||||
struct Array<TArray, const N: usize>;
|
||||
struct Ref<TRef>;
|
||||
struct RefMut<TRefMut>;
|
||||
struct Ptr<TPtr>;
|
||||
struct PtrConst<TPtrConst>;
|
||||
struct PtrMut<TPtrMut>;
|
||||
|
||||
// tuples
|
||||
|
||||
Reference in New Issue
Block a user