From deecb41b0235ac1bb25f011890bc10f8c791601c Mon Sep 17 00:00:00 2001 From: Kevin Stubbings Date: Fri, 20 Dec 2024 15:38:54 -0800 Subject: [PATCH] Squash this later --- go/ql/lib/go.qll | 1 - go/ql/lib/semmle/go/dataflow/SSA.qll | 207 +++++++++++++++++++++++ go/ql/lib/semmle/go/dataflow/SsaImpl.qll | 57 +++++++ 3 files changed, 264 insertions(+), 1 deletion(-) diff --git a/go/ql/lib/go.qll b/go/ql/lib/go.qll index 27509e0e4bbb..df725017dc85 100644 --- a/go/ql/lib/go.qll +++ b/go/ql/lib/go.qll @@ -32,7 +32,6 @@ import semmle.go.frameworks.Afero import semmle.go.frameworks.AwsLambda import semmle.go.frameworks.Beego import semmle.go.frameworks.BeegoOrm -import semmle.go.frameworks.Chi import semmle.go.frameworks.RsCors import semmle.go.frameworks.Couchbase import semmle.go.frameworks.Echo diff --git a/go/ql/lib/semmle/go/dataflow/SSA.qll b/go/ql/lib/semmle/go/dataflow/SSA.qll index 97a9ac4e0697..99ff5cf7d990 100644 --- a/go/ql/lib/semmle/go/dataflow/SSA.qll +++ b/go/ql/lib/semmle/go/dataflow/SSA.qll @@ -37,6 +37,37 @@ class SsaSourceVariable extends LocalVariable { } } +/** + * A variable that can be SSA converted, that is, a global variable + */ +class SsaGlobalSourceVariable extends DeclaredVariable { + SsaGlobalSourceVariable() { not this.getScope() instanceof LocalScope } + + /** + * Holds if there may be indirect references of this variable that are not covered by `getAReference()`. + * + * This is the case for variables that have their address taken, and for variables whose + * name resolution information may be incomplete (for instance due to an extractor error). + */ + predicate mayHaveIndirectReferences() { + // variables that have their address taken + exists(AddressExpr addr | addr.getOperand().stripParens() = this.getAReference()) + or + exists(DataFlow::MethodReadNode mrn | + mrn.getReceiver() = this.getARead() and + mrn.getMethod().getReceiverType() instanceof PointerType + ) + or + // variables where there is an unresolved reference with the same name in the same + // scope or a nested scope, suggesting that name resolution information may be incomplete + exists(FunctionScope scope, FuncDef inner | + scope = this.getScope().(LocalScope).getEnclosingFunctionScope() and + unresolvedReference(this.getName(), inner) and + inner.getScope().getOuterScope*() = scope + ) + } +} + /** * Holds if there is an unresolved reference to `name` in `fn`. */ @@ -99,6 +130,48 @@ class SsaVariable extends TSsaDefinition { } } + +/** + * A global SSA variable. + */ +class SsaGlobalVariable extends TGlobalSsaDefinition { + /** Gets the source variable corresponding to this SSA variable. */ + SsaGlobalSourceVariable getSourceVariable() { result = this.(SsaGlobalDefinition).getSourceVariable() } + + /** Gets the (unique) definition of this SSA variable. */ + SsaGlobalDefinition getDefinition() { result = this } + + /** Gets the type of this SSA variable. */ + Type getType() { result = this.getSourceVariable().getType() } + + /** Gets a use in basic block `bb` that refers to this SSA variable. */ + IR::Instruction getAUseIn(ReachableBasicBlock bb) { + exists(int i, SsaGlobalSourceVariable v | v = this.getSourceVariable() | + result = bb.getNode(i) and + this = getGlobalDefinition(bb, i, v) + ) + } + + /** Gets a use that refers to this SSA variable. */ + IR::Instruction getAUse() { result = this.getAUseIn(_) } + + /** Gets a textual representation of this element. */ + string toString() { result = this.getDefinition().prettyPrintRef() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getDefinition().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + /** * An SSA definition. */ @@ -156,6 +229,63 @@ class SsaDefinition extends TSsaDefinition { ); } +/** + * A global SSA definition. + */ +class SsaGlobalDefinition extends TGlobalSsaDefinition { + /** Gets the SSA variable defined by this definition. */ + SsaGlobalVariable getVariable() { result = this } + + /** Gets the source variable defined by this definition. */ + abstract SsaGlobalSourceVariable getSourceVariable(); + + /** + * Gets the basic block to which this definition belongs. + */ + abstract ReachableBasicBlock getBasicBlock(); + + /** + * INTERNAL: Use `getBasicBlock()` and `getSourceVariable()` instead. + * + * Holds if this is a definition of source variable `v` at index `idx` in basic block `bb`. + * + * Phi nodes are considered to be at index `-1`, all other definitions at the index of + * the control flow node they correspond to. + */ + abstract predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v); + + /** + * INTERNAL: Use `toString()` instead. + * + * Gets a pretty-printed representation of this SSA definition. + */ + abstract string prettyPrintDef(); + + /** + * INTERNAL: Do not use. + * + * Gets a pretty-printed representation of a reference to this SSA definition. + */ + abstract string prettyPrintRef(); + + /** Gets the innermost function or file to which this SSA definition belongs. */ + ControlFlow::Root getRoot() { result = this.getBasicBlock().getRoot() } + + /** Gets a textual representation of this element. */ + string toString() { result = this.prettyPrintDef() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + abstract predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ); +} + /** * An SSA definition that corresponds to an explicit assignment or other variable definition. */ @@ -302,6 +432,14 @@ class SsaPhiNode extends SsaPseudoDefinition, TPhi { } } + +/** + * An SSA variable, possibly with a chain of field reads on it. + */ +private newtype TGlobalSsaWithFields = + TGlobalRoot(SsaGlobalVariable v) or + TGlobalStep(GlobalWithFields base, Field f) { exists(accessPathAux(base, f)) } + /** * An SSA variable, possibly with a chain of field reads on it. */ @@ -401,6 +539,75 @@ class SsaWithFields extends TSsaWithFields { } } + +/** An SSA variable with zero or more fields read from it. */ +class GlobalWithFields extends TSsaWithFields { + /** + * Gets the SSA variable corresponding to the base of this SSA variable with fields. + * + * For example, the SSA variable corresponding to `a` for the SSA variable with fields + * corresponding to `a.b`. + */ + SsaVariable getBaseVariable() { + this = TRoot(result) + or + exists(GlobalWithFields base | this = TStep(base, _) | result = base.getBaseVariable()) + } + + /** Gets a use that refers to this SSA variable with fields. */ + DataFlow::Node getAUse() { this = accessPath(result.asInstruction()) } + + /** Gets the type of this SSA variable with fields. */ + Type getType() { + exists(SsaVariable var | this = TRoot(var) | result = var.getType()) + or + exists(Field f | this = TStep(_, f) | result = f.getType()) + } + + /** Gets a textual representation of this element. */ + string toString() { + exists(SsaVariable var | this = TRoot(var) | result = "(" + var + ")") + or + exists(GlobalWithFields base, Field f | this = TStep(base, f) | result = base + "." + f.getName()) + } + + /** + * Gets an SSA-with-fields variable that is similar to this SSA-with-fields variable in the + * sense that it has the same root variable and the same sequence of field accesses. + */ + GlobalWithFields similar() { + result.getBaseVariable().getSourceVariable() = this.getBaseVariable().getSourceVariable() and + result.getQualifiedName() = this.getQualifiedName() + } + + /** + * Gets the qualified name of the source variable or variable and fields that this represents. + * + * For example, for an SSA variable that represents the field `a.b`, this would get the string + * `"a.b"`. + */ + string getQualifiedName() { + exists(SsaVariable v | this = TRoot(v) and result = v.getSourceVariable().getName()) + or + exists(GlobalWithFields base, Field f | this = TStep(base, f) | + result = base.getQualifiedName() + "." + f.getName() + ) + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getBaseVariable().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + /** * Gets a read similar to `node`, according to the same rules as `SsaWithFields.similar()`. */ diff --git a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll index 0db37ac03ce3..ececb2a3063a 100644 --- a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll @@ -75,6 +75,52 @@ private module Internal { ) } + + + /** + * A data type representing SSA definitions. + * + * We distinguish three kinds of SSA definitions: + * + * 1. Variable definitions, including declarations, assignments and increments/decrements. + * 2. Pseudo-definitions for captured variables at the beginning of the capturing function + * as well as after calls. + * 3. Phi nodes. + * + * SSA definitions are only introduced where necessary. In particular, + * unreachable code has no SSA definitions associated with it, and neither + * have dead assignments (that is, assignments whose value is never read). + */ + cached + newtype TGlobalSsaDefinition = + /** + * An SSA definition that corresponds to an explicit assignment or other variable definition. + */ + TGlobalExplicitDef(ReachableBasicBlock bb, int i, SsaGlobalSourceVariable v) { + defAt(bb, i, v) + + } or + /** + * An SSA definition representing the capturing of an SSA-convertible variable + * in the closure of a nested function. + * + * Capturing definitions appear at the beginning of such functions, as well as + * at any function call that may affect the value of the variable. + */ + TGlobalCapture(ReachableBasicBlock bb, int i, SsaGlobalSourceVariable v) { + mayCapture(bb, i, v) + } or + /** + * An SSA phi node, that is, a pseudo-definition for a variable at a point + * in the flow graph where otherwise two or more definitions for the variable + * would be visible. + */ + TGlbalPhi(ReachableJoinBlock bb, SsaGlobalSourceVariable v) { + liveAtEntry(bb, v) and + inDefDominanceFrontier(bb, v) + } + + /** * Holds if `v` is a captured variable which is declared in `declFun` and read in `useFun`. */ @@ -290,6 +336,17 @@ private module Internal { or rewindReads(bb, i, v) = 1 and result = getDefReachingEndOf(bb.getImmediateDominator(), v) } + + /** + * Gets the unique SSA definition of `v` whose value reaches the `i`th node of `bb`, + * which is a use of `v`. + */ + cached + SsaGlobalDefinition getGlobalDefinition(ReachableBasicBlock bb, int i, SsaSourceVariable v) { + result = getLocalDefinition(bb, i, v) + or + rewindReads(bb, i, v) = 1 and result = getDefReachingEndOf(bb.getImmediateDominator(), v) + } } import Internal