-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Rust: Add barriers for rust/access-invalid-pointer
#20941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,12 +4,15 @@ | |
| */ | ||
|
|
||
| import rust | ||
| private import codeql.rust.elements.Call | ||
| private import codeql.rust.dataflow.DataFlow | ||
| private import codeql.rust.dataflow.FlowSource | ||
| private import codeql.rust.dataflow.FlowSink | ||
| private import codeql.rust.Concepts | ||
| private import codeql.rust.dataflow.internal.Node | ||
| private import codeql.rust.security.Barriers as Barriers | ||
| private import codeql.rust.internal.TypeInference as TypeInference | ||
| private import codeql.rust.internal.Type | ||
|
|
||
| /** | ||
| * Provides default sources, sinks and barriers for detecting accesses to | ||
|
|
@@ -47,20 +50,58 @@ module AccessInvalidPointer { | |
| ModelsAsDataSource() { sourceNode(this, "pointer-invalidate") } | ||
| } | ||
|
|
||
| /** | ||
| * A pointer access using the unary `*` operator. | ||
| */ | ||
| /** A raw pointer access using the unary `*` operator. */ | ||
| private class DereferenceSink extends Sink { | ||
| DereferenceSink() { any(DerefExpr p).getExpr() = this.asExpr() } | ||
| DereferenceSink() { | ||
| exists(Expr p, DerefExpr d | p = d.getExpr() and p = this.asExpr() | | ||
| // Dereferencing a raw pointer is an unsafe operation. Hence relevant | ||
| // dereferences must occur inside code marked as unsafe. | ||
| // See: https://doc.rust-lang.org/reference/types/pointer.html#r-type.pointer.raw.safety | ||
| (p.getEnclosingBlock*().isUnsafe() or p.getEnclosingCallable().(Function).isUnsafe()) and | ||
| (not exists(TypeInference::inferType(p)) or TypeInference::inferType(p) instanceof PtrType) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm interested to see if this |
||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A pointer access from model data. | ||
| */ | ||
| /** A pointer access from model data. */ | ||
| private class ModelsAsDataSink extends Sink { | ||
| ModelsAsDataSink() { sinkNode(this, "pointer-access") } | ||
| } | ||
|
|
||
| private class BarrierCall extends Barrier { | ||
| BarrierCall() { | ||
| exists(Call call, ArgumentPosition pos, string canonicalName | | ||
| call.getStaticTarget().getCanonicalPath() = canonicalName and | ||
| this.asExpr() = call.getArgument(pos) | ||
| | | ||
| canonicalName = "<core::ptr::non_null::NonNull>::new" and pos.asPosition() = 0 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a barrier or a sink??? Either way I'd like to see a test case for this. |
||
| ) | ||
| } | ||
| } | ||
|
|
||
| private class NumericTypeBarrier extends Barrier instanceof Barriers::NumericTypeBarrier { } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this end up blocking legitimate results where a |
||
|
|
||
| private class BooleanTypeBarrier extends Barrier instanceof Barriers::BooleanTypeBarrier { } | ||
|
|
||
| private class FieldlessEnumTypeBarrier extends Barrier instanceof Barriers::FieldlessEnumTypeBarrier | ||
| { } | ||
|
|
||
| private class DefaultBarrier extends Barrier { | ||
| DefaultBarrier() { | ||
| // A barrier for calls that statically resolve to the `Default::default` | ||
| // trait function. Such calls are imprecise, and can always resolve to the | ||
| // implementations for raw pointers that return a null pointer. This | ||
| // creates many false positives in combination with other inaccuracies | ||
| // (too many `pointer-access` sinks created by the model generator). | ||
| // | ||
| // We could try removing this barrier in the future when either 1/ the | ||
| // model generator creates fewer spurious sinks or 2/ data flow for calls | ||
| // to trait functions is more precise. | ||
| this.asExpr().(Call).getStaticTarget().getCanonicalPath() = | ||
| "<_ as core::default::Default>::default" | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to see an example (test case) for this as well. I'm not quite clear what it looks like. |
||
| } | ||
|
|
||
| /** | ||
| * A barrier for invalid pointer access vulnerabilities for values checked to | ||
| * be non-`null`. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this check is better here, in the sink definition, or in the place where the query uses the sink (as it is for
rust/access-after-lifetime-ended)? Should we be consistent between the two?