|
4 | 4 | */ |
5 | 5 |
|
6 | 6 | import rust |
| 7 | +private import codeql.rust.elements.Call |
7 | 8 | private import codeql.rust.dataflow.DataFlow |
8 | 9 | private import codeql.rust.dataflow.FlowSource |
9 | 10 | private import codeql.rust.dataflow.FlowSink |
10 | 11 | private import codeql.rust.Concepts |
11 | 12 | private import codeql.rust.dataflow.internal.Node |
12 | 13 | private import codeql.rust.security.Barriers as Barriers |
| 14 | +private import codeql.rust.internal.TypeInference as TypeInference |
| 15 | +private import codeql.rust.internal.Type |
13 | 16 |
|
14 | 17 | /** |
15 | 18 | * Provides default sources, sinks and barriers for detecting accesses to |
@@ -47,20 +50,58 @@ module AccessInvalidPointer { |
47 | 50 | ModelsAsDataSource() { sourceNode(this, "pointer-invalidate") } |
48 | 51 | } |
49 | 52 |
|
50 | | - /** |
51 | | - * A pointer access using the unary `*` operator. |
52 | | - */ |
| 53 | + /** A raw pointer access using the unary `*` operator. */ |
53 | 54 | private class DereferenceSink extends Sink { |
54 | | - DereferenceSink() { any(DerefExpr p).getExpr() = this.asExpr() } |
| 55 | + DereferenceSink() { |
| 56 | + exists(Expr p, DerefExpr d | p = d.getExpr() and p = this.asExpr() | |
| 57 | + // Dereferencing a raw pointer is an unsafe operation. Hence relevant |
| 58 | + // dereferences must occur inside code marked as unsafe. |
| 59 | + // See: https://doc.rust-lang.org/reference/types/pointer.html#r-type.pointer.raw.safety |
| 60 | + (p.getEnclosingBlock*().isUnsafe() or p.getEnclosingCallable().(Function).isUnsafe()) and |
| 61 | + (not exists(TypeInference::inferType(p)) or TypeInference::inferType(p) instanceof PtrType) |
| 62 | + ) |
| 63 | + } |
55 | 64 | } |
56 | 65 |
|
57 | | - /** |
58 | | - * A pointer access from model data. |
59 | | - */ |
| 66 | + /** A pointer access from model data. */ |
60 | 67 | private class ModelsAsDataSink extends Sink { |
61 | 68 | ModelsAsDataSink() { sinkNode(this, "pointer-access") } |
62 | 69 | } |
63 | 70 |
|
| 71 | + private class BarrierCall extends Barrier { |
| 72 | + BarrierCall() { |
| 73 | + exists(Call call, ArgumentPosition pos, string canonicalName | |
| 74 | + call.getStaticTarget().getCanonicalPath() = canonicalName and |
| 75 | + this.asExpr() = call.getArgument(pos) |
| 76 | + | |
| 77 | + canonicalName = "<core::ptr::non_null::NonNull>::new" and pos.asPosition() = 0 |
| 78 | + ) |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + private class NumericTypeBarrier extends Barrier instanceof Barriers::NumericTypeBarrier { } |
| 83 | + |
| 84 | + private class BooleanTypeBarrier extends Barrier instanceof Barriers::BooleanTypeBarrier { } |
| 85 | + |
| 86 | + private class FieldlessEnumTypeBarrier extends Barrier instanceof Barriers::FieldlessEnumTypeBarrier |
| 87 | + { } |
| 88 | + |
| 89 | + private class DefaultBarrier extends Barrier { |
| 90 | + DefaultBarrier() { |
| 91 | + // A barrier for calls that statically resolve to the `Default::default` |
| 92 | + // trait function. Such calls are imprecise, and can always resolve to the |
| 93 | + // implementations for raw pointers that return a null pointer. This |
| 94 | + // creates many false positives in combination with other inaccuracies |
| 95 | + // (too many `pointer-access` sinks created by the model generator). |
| 96 | + // |
| 97 | + // We could try removing this barrier in the future when either 1/ the |
| 98 | + // model generator creates fewer spurious sinks or 2/ data flow for calls |
| 99 | + // to trait functions is more precise. |
| 100 | + this.asExpr().(Call).getStaticTarget().getCanonicalPath() = |
| 101 | + "<_ as core::default::Default>::default" |
| 102 | + } |
| 103 | + } |
| 104 | + |
64 | 105 | /** |
65 | 106 | * A barrier for invalid pointer access vulnerabilities for values checked to |
66 | 107 | * be non-`null`. |
|
0 commit comments