Data flow through constants

This commit is contained in:
Tom Hvitved
2021-07-01 14:33:37 +02:00
parent 3b6e5881c8
commit bf696df788
7 changed files with 128 additions and 22 deletions

View File

@@ -1,5 +1,6 @@
private import codeql_ruby.AST
private import internal.AST
private import internal.Module
private import internal.Variable
private import internal.TreeSitter
@@ -90,6 +91,32 @@ class ConstantReadAccess extends ConstantAccess {
this = any(AssignOperation a).getLeftOperand()
}
/**
* Gets the value being read, if any. For example, in
*
* ```rb
* module M
* CONST = "const"
* end
*
* puts M::CONST
* ```
*
* the value being read at `M::CONST` is `"const"`.
*/
Expr getValue() {
not exists(this.getScopeExpr()) and
result = lookupConst(this.getEnclosingModule+().getModule(), this.getName()) and
// For now, we restrict the scope of top-level declarations to their file.
// This may remove some plausible targets, but also removes a lot of
// implausible targets
if result.getEnclosingModule() instanceof Toplevel
then result.getFile() = this.getFile()
else any()
or
result = lookupConst(resolveScopeExpr(this.getScopeExpr()), this.getName())
}
final override string getAPrimaryQlClass() { result = "ConstantReadAccess" }
}

View File

@@ -74,6 +74,26 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
result = this.getAModule() and result.getName() = name
}
/**
* Gets the value of the constant named `name`, if any.
*
* For example, the value of `CONST` is `"const"` in
* ```rb
* module M
* CONST = "const"
* end
* ```
*/
Expr getConstant(string name) {
exists(AssignExpr ae, ConstantWriteAccess w |
ae = this.getAStmt() and
w = ae.getLeftOperand() and
w.getName() = name and
not exists(w.getScopeExpr()) and
result = ae.getRightOperand()
)
}
/** Gets the representation of the run-time value of this module or class. */
Module getModule() { none() }
}

View File

@@ -130,6 +130,12 @@ private module Cached {
)
)
}
cached
Method lookupMethod(Module m, string name) { TMethod(result) = lookupMethodOrConst(m, name) }
cached
Expr lookupConst(Module m, string name) { TExpr(result) = lookupMethodOrConst(m, name) }
}
import Cached
@@ -340,24 +346,39 @@ private Module getAncestors(Module m) {
result = getAncestors(m.getAPrependedModule())
}
Method getMethod(TModule owner, string name) {
exists(ModuleBase m | m.getModule() = owner and result = m.getMethod(name))
}
private newtype TMethodOrExpr =
TMethod(Method m) or
TExpr(Expr e)
private Method lookupMethod0(Module m, string name) {
result = lookupMethod0(m.getAPrependedModule(), name)
or
not exists(getMethod(getAncestors(m.getAPrependedModule()), name)) and
(
result = getMethod(m, name)
private TMethodOrExpr getMethodOrConst(TModule owner, string name) {
exists(ModuleBase m | m.getModule() = owner |
result = TMethod(m.getMethod(name))
or
not exists(getMethod(m, name)) and result = lookupMethod0(m.getAnIncludedModule(), name)
result = TExpr(m.getConstant(name))
)
}
Method lookupMethod(Module m, string name) {
result = lookupMethod0(m, name)
or
not exists(lookupMethod0(m, name)) and
result = lookupMethod(m.getSuperClass(), name)
module ExposedForTestingOnly {
Method getMethod(TModule owner, string name) { TMethod(result) = getMethodOrConst(owner, name) }
Expr getConst(TModule owner, string name) { TExpr(result) = getMethodOrConst(owner, name) }
}
private TMethodOrExpr lookupMethodOrConst0(Module m, string name) {
result = lookupMethodOrConst0(m.getAPrependedModule(), name)
or
not exists(getMethodOrConst(getAncestors(m.getAPrependedModule()), name)) and
(
result = getMethodOrConst(m, name)
or
not exists(getMethodOrConst(m, name)) and
result = lookupMethodOrConst0(m.getAnIncludedModule(), name)
)
}
private TMethodOrExpr lookupMethodOrConst(Module m, string name) {
result = lookupMethodOrConst0(m, name)
or
not exists(lookupMethodOrConst0(m, name)) and
result = lookupMethodOrConst(m.getSuperClass(), name)
}

View File

@@ -455,6 +455,8 @@ predicate jumpStep(Node pred, Node succ) {
m = s.getEnclosingMethod() and
m != s.getEnclosingCallable()
)
or
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
}
predicate storeStep(Node node1, Content c, Node node2) { none() }

View File

@@ -1,3 +1,4 @@
constantAccess
| constants.rb:1:1:15:3 | ModuleA | write | ModuleA | ModuleDeclaration |
| constants.rb:2:5:4:7 | ClassA | write | ClassA | ClassDeclaration |
| constants.rb:3:9:3:15 | CONST_A | write | CONST_A | ConstantAssignment |
@@ -27,3 +28,28 @@
| constants.rb:37:1:37:7 | ModuleA | read | ModuleA | ConstantReadAccess |
| constants.rb:37:1:37:16 | ModuleB | read | ModuleB | ConstantReadAccess |
| constants.rb:37:1:37:26 | MAX_SIZE | write | MAX_SIZE | ConstantAssignment |
getConst
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
lookupConst
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
| constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:9:9:10:11 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:31:1:32:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" |
| constants.rb:31:1:32:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Array | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Class | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Complex | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | FalseClass | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Float | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Hash | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Integer | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Module | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | NilClass | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Numeric | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | Rational | GREETING | constants.rb:17:12:17:64 | ... + ... |
| file://:0:0:0:0 | TrueClass | GREETING | constants.rb:17:12:17:64 | ... + ... |

View File

@@ -1,8 +1,16 @@
import ruby
import codeql_ruby.ast.internal.Module as M
from ConstantAccess a, string kind
where
a instanceof ConstantReadAccess and kind = "read"
or
a instanceof ConstantWriteAccess and kind = "write"
select a, kind, a.getName(), a.getAPrimaryQlClass()
query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) {
(
a instanceof ConstantReadAccess and kind = "read"
or
a instanceof ConstantWriteAccess and kind = "write"
) and
name = a.getName() and
cls = a.getAPrimaryQlClass()
}
query Expr getConst(Module m, string name) { result = M::ExposedForTestingOnly::getConst(m, name) }
query Expr lookupConst(Module m, string name) { result = M::lookupConst(m, name) }

View File

@@ -1,6 +1,8 @@
import ruby
import codeql_ruby.ast.internal.Module as M
query MethodBase getMethod(Module m, string name) { result = M::getMethod(m, name) }
query MethodBase getMethod(Module m, string name) {
result = M::ExposedForTestingOnly::getMethod(m, name)
}
query MethodBase lookupMethod(Module m, string name) { result = M::lookupMethod(m, name) }