From 97d0220ffda0b93118680538c5b62dcaecf843bd Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 25 Nov 2020 16:48:53 +0100 Subject: [PATCH] CFG: Model nodes with simple flow --- .../internal/ControlFlowGraphImpl.qll | 271 +++++++++++++++++- .../controlflow/graph/Cfg.expected | 71 +++++ .../library-tests/controlflow/graph/cfg.rb | 22 ++ 3 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 ql/test/library-tests/controlflow/graph/cfg.rb diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 16dbce5ccd6..4d56262cfd0 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -228,6 +228,14 @@ abstract private class LeafTree extends PreOrderTree, PostOrderTree { /** Defines the CFG by dispatch on the various AST types. */ private module Trees { + private class AliasTree extends StandardPreOrderTree, Alias { + final override AstNode getChildNode(int i) { + i = 0 and result = this.getName() + or + i = 1 and result = this.getAlias() + } + } + private class ArgumentListTree extends StandardPostOrderTree, ArgumentList { final override AstNode getChildNode(int i) { result = this.getChild(i) } @@ -246,6 +254,22 @@ private module Trees { } } + private class BareStringTree extends StandardPostOrderTree, BareString { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class BareSymbolTree extends StandardPostOrderTree, BareSymbol { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class BeginTree extends StandardPreOrderTree, Begin { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + private class BeginBlockTree extends StandardPreOrderTree, BeginBlock { final override AstNode getChildNode(int i) { result = this.getChild(i) } } @@ -260,7 +284,21 @@ private module Trees { } } - private class BlockParametersTree extends LeafTree, BlockParameters { + private class BlockTree extends StandardPreOrderTree, Block { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class BlockArgumentTree extends StandardPostOrderTree, BlockArgument { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class BlockParameterTree extends LeafTree, BlockParameter { } + + private class BlockParametersTree extends StandardPreOrderTree, BlockParameters { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + override predicate isHidden() { any() } } @@ -314,10 +352,33 @@ private module Trees { } } + private class ChainedStringTree extends StandardPostOrderTree, ChainedString { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class CharacterTree extends LeafTree, Character { } + private class ClassTree extends StandardPreOrderTree, Class { final override AstNode getChildNode(int i) { result = this.getChild(i) } } + private class ClassVariableTree extends LeafTree, ClassVariable { } + + private class ComplexTree extends LeafTree, Complex { } + + private class ConstantTree extends LeafTree, Constant { } + + private class DestructuredLeftAssignmentTree extends StandardPostOrderTree, + DestructuredLeftAssignment { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + + private class DestructuredParameterTree extends StandardPostOrderTree, DestructuredParameter { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + private class DoTree extends StandardPreOrderTree, Do { final override AstNode getChildNode(int i) { result = this.getChild(i) } @@ -330,16 +391,50 @@ private module Trees { override predicate isHidden() { any() } } + private class ElementReferenceTree extends StandardPostOrderTree, ElementReference { + final override AstNode getChildNode(int i) { + i = 0 and result = this.getObject() + or + result = this.getChild(i - 1) + } + } + private class ElseTree extends StandardPreOrderTree, Else { final override AstNode getChildNode(int i) { result = this.getChild(i) } override predicate isHidden() { any() } } + private class EmptyStatementTree extends LeafTree, EmptyStatement { } + private class EndBlockTree extends StandardPreOrderTree, EndBlock { final override AstNode getChildNode(int i) { result = this.getChild(i) } } + private class EnsureTree extends StandardPreOrderTree, Ensure { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class EscapeSequenceTree extends LeafTree, EscapeSequence { } + + private class ExceptionVariableTree extends StandardPostOrderTree, ExceptionVariable { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + + override predicate isHidden() { any() } + } + + private class ExceptionsTree extends StandardPostOrderTree, Exceptions { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class FalseTree extends LeafTree, False { } + + private class FloatTree extends LeafTree, Float { } + /** * Control flow of a for-in loop * @@ -431,6 +526,18 @@ private module Trees { } } + private class GlobalVariableTree extends LeafTree, GlobalVariable { } + + private class HashTree extends StandardPostOrderTree, Hash { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + + private class HashSplatArgumentTree extends StandardPostOrderTree, HashSplatArgument { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class HashSplatParameterTree extends LeafTree, HashSplatParameter { } + private class HeredocBeginningTree extends StandardPreOrderTree, HeredocBeginning { final override AstNode getChildNode(int i) { i = 0 and @@ -452,6 +559,16 @@ private module Trees { } } + private class HeredocBodyTree extends StandardPostOrderTree, HeredocBody { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class HeredocContentTree extends LeafTree, HeredocContent { } + + private class HeredocEndTree extends LeafTree, HeredocEnd { } + private class IdentifierTree extends LeafTree, Identifier { } private class IfElsifTree extends PreOrderTree, IfElsifAstNode { @@ -491,8 +608,40 @@ private module Trees { override predicate isHidden() { any() } } + private class InstanceVariableTree extends LeafTree, InstanceVariable { } + private class IntegerTree extends LeafTree, Integer { } + private class InterpolationTree extends StandardPostOrderTree, Interpolation { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class KeywordParameterTree extends StandardPostOrderTree, KeywordParameter { + final override AstNode getChildNode(int i) { result = this.getValue() and i = 0 } + } + + private class LambdaTree extends StandardPreOrderTree, Lambda { + final override AstNode getChildNode(int i) { + result = this.getParameters() and i = 0 + or + result = this.getBody() and i = 1 + } + + override predicate isHidden() { any() } + } + + private class LambdaParametersTree extends StandardPreOrderTree, LambdaParameters { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class LeftAssignmentListTree extends StandardPostOrderTree, LeftAssignmentList { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + class LogicalAndTree extends PostOrderTree, LogicalAndAstNode { final override predicate propagatesAbnormal(AstNode child) { child in [left, right] } @@ -564,6 +713,12 @@ private module Trees { } } + private class MethodParametersTree extends StandardPreOrderTree, MethodParameters { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + private class ModuleTree extends StandardPreOrderTree, Module { final override AstNode getChildNode(int i) { result = this.getChild(i) } } @@ -572,6 +727,8 @@ private module Trees { final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } } + private class NilTree extends LeafTree, Nil { } + private class OperatorAssignmentTree extends StandardPostOrderTree, OperatorAssignment { final override AstNode getChildNode(int i) { result = this.getLeft() and i = 0 @@ -580,6 +737,18 @@ private module Trees { } } + private class OptionalParameterTree extends StandardPostOrderTree, OptionalParameter { + final override AstNode getChildNode(int i) { result = this.getValue() and i = 0 } + } + + private class PairTree extends StandardPostOrderTree, Pair { + final override AstNode getChildNode(int i) { + result = this.getKey() and i = 0 + or + result = this.getValue() and i = 1 + } + } + private class ParenthesizedStatementsTree extends StandardPostOrderTree, ParenthesizedStatements { final override AstNode getChildNode(int i) { result = this.getChild(i) } } @@ -594,10 +763,32 @@ private module Trees { override predicate isHidden() { any() } } + private class RangeTree extends StandardPostOrderTree, Range { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + + private class RationalTree extends LeafTree, Rational { } + private class RedoTree extends StandardPostOrderTree, Redo { final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } } + private class RegexTree extends StandardPostOrderTree, Regex { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class RescueTree extends StandardPreOrderTree, Rescue { + final override AstNode getChildNode(int i) { + result = this.getExceptions() and i = 0 + or + result = this.getVariable() and i = 1 + or + result = this.getBody() and i = 2 + } + } + private class RescueModifierTree extends PreOrderTree, RescueModifier { final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } @@ -619,10 +810,36 @@ private module Trees { } } + private class RestAssignmentTree extends StandardPostOrderTree, RestAssignment { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class RetryTree extends StandardPreOrderTree, Retry { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + private class ReturnTree extends StandardPostOrderTree, Return { final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } } + private class RightAssignmentListTree extends StandardPostOrderTree, RightAssignmentList { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + + override predicate isHidden() { any() } + } + + private class ScopeResolutionTree extends StandardPostOrderTree, ScopeResolution { + final override AstNode getChildNode(int i) { + result = this.getScope() and i = 0 + or + result = this.getName() and i = 1 + } + } + + private class SelfTree extends LeafTree, Self { } + + private class SetterTree extends LeafTree, Setter { } + private class SingletonClassTree extends StandardPreOrderTree, SingletonClass { final override AstNode getChildNode(int i) { result = this.getChild(i) } @@ -635,7 +852,47 @@ private module Trees { override predicate isHidden() { any() } } - private class StringTree extends LeafTree, String { } + private class SplatArgumentTree extends StandardPostOrderTree, SplatArgument { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class SplatParameterTree extends StandardPostOrderTree, SplatArgument { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class StringTree extends StandardPostOrderTree, String { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class StringArrayTree extends StandardPostOrderTree, StringArray { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + + private class StringContentTree extends LeafTree, StringContent { } + + private class SubshellTree extends StandardPostOrderTree, Subshell { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class SuperTree extends LeafTree, Super { } + + private class SuperclassTree extends StandardPostOrderTree, Superclass { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } + } + + private class SymbolTree extends StandardPostOrderTree, Symbol { + final override AstNode getChildNode(int i) { + result = this.getChild(i) and result instanceof Interpolation + } + } + + private class SymbolArrayTree extends StandardPostOrderTree, SymbolArray { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } private class ThenTree extends StandardPreOrderTree, Then { final override AstNode getChildNode(int i) { result = this.getChild(i) } @@ -643,12 +900,18 @@ private module Trees { override predicate isHidden() { any() } } + private class TrueTree extends LeafTree, True { } + private class UnaryTree extends StandardPostOrderTree, Unary { UnaryTree() { not this instanceof LogicalNotAstNode } final override AstNode getChildNode(int i) { result = this.getOperand() and i = 0 } } + private class UndefTree extends StandardPreOrderTree, Undef { + final override AstNode getChildNode(int i) { result = this.getChild(i) } + } + private class WhenTree extends PreOrderTree, When { final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern(_) } @@ -718,6 +981,10 @@ private module Trees { } } +private class YieldTree extends StandardPreOrderTree, Yield { + final override AstNode getChildNode(int i) { result = this.getChild() and i = 0 } +} + cached private module Cached { private predicate isAbnormalExitType(SuccessorType t) { diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index fb904928151..c39bf86ea20 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,4 +1,41 @@ nodes +| cfg.rb:1:1:22:2 | enter top-level | +| cfg.rb:1:1:22:2 | exit top-level | +| cfg.rb:1:1:22:2 | exit top-level (normal) | +| cfg.rb:3:1:3:13 | Alias | +| cfg.rb:3:7:3:9 | foo | +| cfg.rb:3:11:3:13 | bar | +| cfg.rb:5:1:5:1 | b | +| cfg.rb:5:1:5:6 | Assignment | +| cfg.rb:5:5:5:6 | 42 | +| cfg.rb:7:1:7:21 | SymbolArray | +| cfg.rb:7:4:7:12 | BareSymbol | +| cfg.rb:7:7:7:12 | Interpolation | +| cfg.rb:7:10:7:10 | b | +| cfg.rb:7:14:7:20 | BareSymbol | +| cfg.rb:9:1:9:21 | StringArray | +| cfg.rb:9:4:9:12 | BareString | +| cfg.rb:9:7:9:12 | Interpolation | +| cfg.rb:9:10:9:10 | b | +| cfg.rb:9:14:9:20 | BareString | +| cfg.rb:12:1:14:3 | Begin | +| cfg.rb:13:3:13:6 | puts | +| cfg.rb:13:3:13:8 | MethodCall | +| cfg.rb:13:8:13:8 | 4 | +| cfg.rb:16:1:18:1 | BeginBlock | +| cfg.rb:16:1:18:1 | enter BEGIN block | +| cfg.rb:16:1:18:1 | exit BEGIN block | +| cfg.rb:16:1:18:1 | exit BEGIN block (normal) | +| cfg.rb:17:3:17:6 | puts | +| cfg.rb:17:3:17:14 | MethodCall | +| cfg.rb:17:8:17:14 | String | +| cfg.rb:20:1:22:1 | EndBlock | +| cfg.rb:20:1:22:1 | enter END block | +| cfg.rb:20:1:22:1 | exit END block | +| cfg.rb:20:1:22:1 | exit END block (normal) | +| cfg.rb:21:3:21:6 | puts | +| cfg.rb:21:3:21:14 | MethodCall | +| cfg.rb:21:8:21:14 | String | | exit.rb:1:1:6:3 | enter m1 | | exit.rb:1:1:6:3 | exit m1 | | exit.rb:1:1:6:3 | exit m1 (abnormal) | @@ -171,6 +208,40 @@ nodes | raise.rb:5:3:5:15 | MethodCall | | raise.rb:5:8:5:15 | String | edges +| cfg.rb:1:1:22:2 | enter top-level | cfg.rb:3:1:3:13 | Alias | semmle.label | successor | +| cfg.rb:1:1:22:2 | exit top-level (normal) | cfg.rb:1:1:22:2 | exit top-level | semmle.label | successor | +| cfg.rb:3:1:3:13 | Alias | cfg.rb:3:7:3:9 | foo | semmle.label | successor | +| cfg.rb:3:7:3:9 | foo | cfg.rb:3:11:3:13 | bar | semmle.label | successor | +| cfg.rb:3:11:3:13 | bar | cfg.rb:5:5:5:6 | 42 | semmle.label | successor | +| cfg.rb:5:1:5:1 | b | cfg.rb:5:1:5:6 | Assignment | semmle.label | successor | +| cfg.rb:5:1:5:6 | Assignment | cfg.rb:7:10:7:10 | b | semmle.label | successor | +| cfg.rb:5:5:5:6 | 42 | cfg.rb:5:1:5:1 | b | semmle.label | successor | +| cfg.rb:7:1:7:21 | SymbolArray | cfg.rb:9:10:9:10 | b | semmle.label | successor | +| cfg.rb:7:4:7:12 | BareSymbol | cfg.rb:7:14:7:20 | BareSymbol | semmle.label | successor | +| cfg.rb:7:7:7:12 | Interpolation | cfg.rb:7:4:7:12 | BareSymbol | semmle.label | successor | +| cfg.rb:7:10:7:10 | b | cfg.rb:7:7:7:12 | Interpolation | semmle.label | successor | +| cfg.rb:7:14:7:20 | BareSymbol | cfg.rb:7:1:7:21 | SymbolArray | semmle.label | successor | +| cfg.rb:9:1:9:21 | StringArray | cfg.rb:12:1:14:3 | Begin | semmle.label | successor | +| cfg.rb:9:4:9:12 | BareString | cfg.rb:9:14:9:20 | BareString | semmle.label | successor | +| cfg.rb:9:7:9:12 | Interpolation | cfg.rb:9:4:9:12 | BareString | semmle.label | successor | +| cfg.rb:9:10:9:10 | b | cfg.rb:9:7:9:12 | Interpolation | semmle.label | successor | +| cfg.rb:9:14:9:20 | BareString | cfg.rb:9:1:9:21 | StringArray | semmle.label | successor | +| cfg.rb:12:1:14:3 | Begin | cfg.rb:13:8:13:8 | 4 | semmle.label | successor | +| cfg.rb:13:3:13:6 | puts | cfg.rb:13:3:13:8 | MethodCall | semmle.label | successor | +| cfg.rb:13:3:13:8 | MethodCall | cfg.rb:1:1:22:2 | exit top-level (normal) | semmle.label | successor | +| cfg.rb:13:8:13:8 | 4 | cfg.rb:13:3:13:6 | puts | semmle.label | successor | +| cfg.rb:16:1:18:1 | BeginBlock | cfg.rb:17:8:17:14 | String | semmle.label | successor | +| cfg.rb:16:1:18:1 | enter BEGIN block | cfg.rb:16:1:18:1 | BeginBlock | semmle.label | successor | +| cfg.rb:16:1:18:1 | exit BEGIN block (normal) | cfg.rb:16:1:18:1 | exit BEGIN block | semmle.label | successor | +| cfg.rb:17:3:17:6 | puts | cfg.rb:17:3:17:14 | MethodCall | semmle.label | successor | +| cfg.rb:17:3:17:14 | MethodCall | cfg.rb:16:1:18:1 | exit BEGIN block (normal) | semmle.label | successor | +| cfg.rb:17:8:17:14 | String | cfg.rb:17:3:17:6 | puts | semmle.label | successor | +| cfg.rb:20:1:22:1 | EndBlock | cfg.rb:21:8:21:14 | String | semmle.label | successor | +| cfg.rb:20:1:22:1 | enter END block | cfg.rb:20:1:22:1 | EndBlock | semmle.label | successor | +| cfg.rb:20:1:22:1 | exit END block (normal) | cfg.rb:20:1:22:1 | exit END block | semmle.label | successor | +| cfg.rb:21:3:21:6 | puts | cfg.rb:21:3:21:14 | MethodCall | semmle.label | successor | +| cfg.rb:21:3:21:14 | MethodCall | cfg.rb:20:1:22:1 | exit END block (normal) | semmle.label | successor | +| cfg.rb:21:8:21:14 | String | cfg.rb:21:3:21:6 | puts | semmle.label | successor | | exit.rb:1:1:6:3 | enter m1 | exit.rb:2:3:4:5 | If | semmle.label | successor | | exit.rb:1:1:6:3 | exit m1 (abnormal) | exit.rb:1:1:6:3 | exit m1 | semmle.label | successor | | exit.rb:1:1:6:3 | exit m1 (normal) | exit.rb:1:1:6:3 | exit m1 | semmle.label | successor | diff --git a/ql/test/library-tests/controlflow/graph/cfg.rb b/ql/test/library-tests/controlflow/graph/cfg.rb new file mode 100644 index 00000000000..10bbdd2280b --- /dev/null +++ b/ql/test/library-tests/controlflow/graph/cfg.rb @@ -0,0 +1,22 @@ +def bar; end + +alias foo bar + +b = 42 + +%I(one#{ b } another) # bare symbol + +%W(one#{ b } another) # bare string + + +begin + puts 4 +end + +BEGIN { + puts "hello" +} + +END { + puts "world" +}