Merge branch 'main' into bazookamusic/cwe-1427

This commit is contained in:
Sotiris Dragonas
2026-06-17 14:55:35 +03:00
committed by GitHub
21 changed files with 333 additions and 179 deletions

View File

@@ -2,7 +2,7 @@
* @github/code-scanning-alert-coverage
# CodeQL language libraries
/actions/ @github/codeql-dynamic
/actions/ @github/code-scanning-alert-coverage
/cpp/ @github/codeql-c-analysis
/csharp/ @github/codeql-csharp
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor @github/code-scanning-language-coverage

View File

@@ -1,8 +1,8 @@
/**
* @name Checkout of untrusted code in a trusted context
* @description Privileged workflows have read/write access to the base repository and access to secrets.
* By explicitly checking out and running the build script from a fork the untrusted code is running in an environment
* that is able to push to the base repository and to access secrets.
* @name Checkout of untrusted code in a non-privileged context
* @description Checking out and running the build script from a fork executes untrusted code. Even in a
* non-privileged workflow, this can be abused, for example to compromise self-hosted runners
* or to poison caches and artifacts that are later consumed by privileged workflows.
* @kind problem
* @problem.severity warning
* @precision medium
@@ -20,4 +20,4 @@ from PRHeadCheckoutStep checkout
where
// the checkout occurs in a non-privileged context
inNonPrivilegedContext(checkout)
select checkout, "Potential unsafe checkout of untrusted pull request on privileged workflow."
select checkout, "Potential unsafe checkout of untrusted pull request on non-privileged workflow."

View File

@@ -0,0 +1,4 @@
---
category: queryMetadata
---
* The name, description, and alert message of `actions/untrusted-checkout/medium` have been corrected to describe a non-privileged context.

View File

@@ -1,10 +1,10 @@
| .github/workflows/artifactpoisoning81.yml:11:9:14:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/dependabot2.yml:33:9:38:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/mend.yml:22:9:29:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/poc3.yml:18:7:25:4 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/poc.yml:30:9:36:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/priv_pull_request_checkout.yml:14:9:20:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/test3.yml:28:9:33:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/test4.yml:18:7:25:4 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/test8.yml:20:9:26:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/test9.yml:11:9:16:6 | Uses Step | Potential unsafe checkout of untrusted pull request on privileged workflow. |
| .github/workflows/artifactpoisoning81.yml:11:9:14:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/dependabot2.yml:33:9:38:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/mend.yml:22:9:29:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/poc3.yml:18:7:25:4 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/poc.yml:30:9:36:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/priv_pull_request_checkout.yml:14:9:20:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/test3.yml:28:9:33:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/test4.yml:18:7:25:4 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/test8.yml:20:9:26:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |
| .github/workflows/test9.yml:11:9:16:6 | Uses Step | Potential unsafe checkout of untrusted pull request on non-privileged workflow. |

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* `FuncTypeExpr.getResultDecl()` has been deprecated. Use `FuncTypeExpr.getResultDecl(int i)` instead.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `DataFlow::ResultNode`s are no longer created for returned expressions in functions with named result parameters. In this case there are already result nodes corresponding to `IR::ReadResultInstruction`s at the end of the function body.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `FuncTypeExpr.getNumResult()` now gets the number of result parameters. It previously got the number of result declarations, which is different when one result declaration declares more than one variable, as in `x, y int`. All uses of it expected the number of result parameters. Its QLDoc has been updated.

View File

@@ -1049,17 +1049,29 @@ class FuncTypeExpr extends @functypeexpr, TypeExpr, ScopeNode, FieldParent {
*/
int getNumParameter() { result = count(this.getAParameterDecl().getANameExpr()) }
/** Gets the `i`th result of this function type (0-based). */
/**
* Gets the `i`th result declaration of this function type (0-based).
*
* Note: `x, y int` is a single `ResultVariableDecl`.
*/
ResultVariableDecl getResultDecl(int i) { result = this.getField(-(i + 1)) }
/** Gets a result of this function type. */
/**
* Gets a result declaration of this function type.
*
* Note: `x, y int` is a single `ResultVariableDecl`.
*/
ResultVariableDecl getAResultDecl() { result = this.getResultDecl(_) }
/** Gets the number of results of this function type. */
int getNumResult() { result = count(this.getAResultDecl()) }
/** Gets the number of result parameters of this function type. */
int getNumResult() { result = count(this.getAResultDecl().getANameExpr()) }
/** Gets the result of this function type, if there is only one. */
ResultVariableDecl getResultDecl() { this.getNumResult() = 1 and result = this.getAResultDecl() }
/**
* DEPRECATED: Use `getResultDecl(int i)` instead.
*/
deprecated ResultVariableDecl getResultDecl() {
this.getNumResult() = 1 and result = this.getAResultDecl()
}
override string toString() { result = "function type" }

View File

@@ -923,15 +923,20 @@ module Public {
/**
* A node whose value is returned as a result from a function.
*
* This can either be a node corresponding to an expression in a return statement,
* or a node representing the current value of a named result variable at the exit
* of the function.
* If the function declares named result variables, this is a node representing
* the current value of one of those variables at function exit. Otherwise, this
* is a node corresponding to an expression in a return statement.
*/
class ResultNode extends InstructionNode {
int i;
ResultNode() {
exists(FuncDef fd |
// If the function has named result variables, then the
// `IR::ReadResultInstruction` nodes at the end of the function are
// the correct result nodes. Otherwise, the returned expressions are
// the result nodes.
not exists(fd.getAResultVar()) and
exists(IR::ReturnInstruction ret | ret.getRoot() = fd | insn = ret.getResult(i))
or
insn.(IR::ReadResultInstruction).reads(fd.getResultVar(i))

View File

@@ -55,7 +55,7 @@ class SyncFileFun extends Method {
/**
* Holds if a `call` to a function is "unhandled". That is, it is either
* deferred or its result is not assigned to anything.
* deferred or used as an expression statement, so that its result is discarded.
*
* TODO: maybe we should check that something is actually done with the result
*/
@@ -77,7 +77,6 @@ predicate isWritableFileHandle(DataFlow::Node source, DataFlow::CallNode call) {
// get the flags expression used for opening the file
call.getArgument(1) = flags and
// extract individual flags from the argument
// flag = flag.getAChild*() and
flag = getConstants(flags.asExpr()) and
// check for one which signals that the handle will be writable
// note that we are underestimating here, since the flags may be
@@ -87,27 +86,18 @@ predicate isWritableFileHandle(DataFlow::Node source, DataFlow::CallNode call) {
}
/**
* Holds if `os.File.Close` is called on `sink`.
* Holds if `postDominator` post-dominates `node` in the control-flow graph. That is,
* every path from `node` to the exit of the enclosing function passes through
* `postDominator`.
*/
predicate isCloseSink(DataFlow::Node sink, DataFlow::CallNode closeCall) {
// find calls to the os.File.Close function
closeCall = any(CloseFileFun f).getACall() and
// that are unhandled
unhandledCall(closeCall) and
// where the function is called on the sink
closeCall.getReceiver() = sink and
// and check that it is not dominated by a call to `os.File.Sync`.
// TODO: fix this logic when `closeCall` is in a defer statement.
not exists(IR::Instruction syncInstr, DataFlow::Node syncReceiver, DataFlow::CallNode syncCall |
// match the instruction corresponding to an `os.File.Sync` call with the predecessor
syncCall.asInstruction() = syncInstr and
// check that the call to `os.File.Sync` is handled
isHandledSync(syncReceiver, syncCall) and
// find a predecessor to `closeCall` in the control flow graph which dominates the call to
// `os.File.Close`
syncInstr.dominatesNode(closeCall.asInstruction()) and
// check that `os.File.Sync` is called on the same object as `os.File.Close`
exists(DataFlow::SsaNode ssa | ssa.getAUse() = sink and ssa.getAUse() = syncReceiver)
pragma[inline]
predicate postDominatesNode(ControlFlow::Node postDominator, ControlFlow::Node node) {
exists(ReachableBasicBlock pdbb, ReachableBasicBlock nbb, int i, int j |
postDominator = pdbb.getNode(i) and node = nbb.getNode(j)
|
pdbb.strictlyPostDominates(nbb)
or
pdbb = nbb and i >= j
)
}
@@ -127,7 +117,39 @@ predicate isHandledSync(DataFlow::Node sink, DataFlow::CallNode syncCall) {
module UnhandledFileCloseConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isWritableFileHandle(source, _) }
predicate isSink(DataFlow::Node sink) { isCloseSink(sink, _) }
predicate isSink(DataFlow::Node sink) {
exists(DataFlow::CallNode closeCall |
// `closeCall` is an unhandled call to `os.File.Close` on `sink`
closeCall = any(CloseFileFun f).getACall() and
unhandledCall(closeCall) and
closeCall.getReceiver() = sink
|
// `closeCall` is not guaranteed to be preceded during
// execution by a handled call to `os.File.Sync` on the same file handle.
not exists(DataFlow::Node syncReceiver, DataFlow::CallNode syncCall |
// check that the call to `os.File.Sync` is handled
isHandledSync(syncReceiver, syncCall) and
// check that `os.File.Sync` is called on the same object as `os.File.Close`
exists(DataFlow::SsaNode ssa | ssa.getAUse() = sink and ssa.getAUse() = syncReceiver)
|
if exists(DeferStmt defer | defer.getCall() = closeCall.asExpr())
then
// When the call to `os.File.Close` is deferred it runs when the enclosing function
// returns, but the receiver of the deferred call is evaluated where the `defer`
// statement appears. It is therefore enough for the handled call to `os.File.Sync`
// to post-dominate that point, since that guarantees `os.File.Sync` runs before the
// deferred `os.File.Close` on every path on which the `os.File.Close` is registered.
// We cannot reuse the domination check below because the control-flow graph splices
// the deferred call in at the function exit, where it may be reachable along paths
// that do not pass through the call to `os.File.Sync`.
postDominatesNode(syncCall.asInstruction(), sink.asInstruction())
else
// Otherwise the call to `os.File.Close` is executed where it appears, so we require
// the handled call to `os.File.Sync` to dominate it.
syncCall.asInstruction().dominatesNode(closeCall.asInstruction())
)
)
}
predicate observeDiffInformedIncrementalMode() { any() }
@@ -148,14 +170,12 @@ import UnhandledFileCloseFlow::PathGraph
from
UnhandledFileCloseFlow::PathNode source, DataFlow::CallNode openCall,
UnhandledFileCloseFlow::PathNode sink, DataFlow::CallNode closeCall
UnhandledFileCloseFlow::PathNode sink
where
// find data flow from an `os.OpenFile` call to an `os.File.Close` call
// where the handle is writable
UnhandledFileCloseFlow::flowPath(source, sink) and
isWritableFileHandle(source.getNode(), openCall) and
// get the `CallNode` corresponding to the sink
isCloseSink(sink.getNode(), closeCall)
isWritableFileHandle(source.getNode(), openCall)
select sink, source, sink,
"File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly.",
openCall, openCall.toString()

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The query `go/unhandled-writable-file-close` ("Writable file handle closed without error handling") now produces fewer false positives. A deferred call to `Close` that is preceded on every execution path by a handled call to `Sync` on the same file handle is no longer flagged.

View File

@@ -735,129 +735,153 @@
| main.go:48:11:48:12 | 42 | main.go:48:2:48:7 | assignment to result |
| main.go:49:2:49:7 | return statement | main.go:47:13:47:18 | implicit read of result |
| main.go:52:1:54:1 | entry | main.go:52:14:52:19 | zero value for result |
| main.go:52:1:54:1 | function declaration | main.go:56:6:56:10 | skip |
| main.go:52:1:54:1 | function declaration | main.go:56:6:56:9 | skip |
| main.go:52:6:52:9 | skip | main.go:52:1:54:1 | function declaration |
| main.go:52:14:52:19 | implicit read of result | main.go:52:1:54:1 | exit |
| main.go:52:14:52:19 | initialization of result | main.go:53:2:53:7 | return statement |
| main.go:52:14:52:19 | zero value for result | main.go:52:14:52:19 | initialization of result |
| main.go:53:2:53:7 | return statement | main.go:52:14:52:19 | implicit read of result |
| main.go:56:1:80:1 | entry | main.go:57:6:57:6 | skip |
| main.go:56:1:80:1 | function declaration | main.go:82:6:82:13 | skip |
| main.go:56:6:56:10 | skip | main.go:56:1:80:1 | function declaration |
| main.go:57:6:57:6 | assignment to x | main.go:58:6:58:9 | cond |
| main.go:57:6:57:6 | skip | main.go:57:6:57:6 | zero value for x |
| main.go:57:6:57:6 | zero value for x | main.go:57:6:57:6 | assignment to x |
| main.go:58:6:58:9 | cond | main.go:58:6:58:11 | call to cond |
| main.go:58:6:58:11 | call to cond | main.go:56:1:80:1 | exit |
| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | call to cond is false |
| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | call to cond is true |
| main.go:58:6:58:11 | call to cond is false | main.go:61:2:61:10 | selection of Print |
| main.go:58:6:58:11 | call to cond is true | main.go:59:3:59:3 | skip |
| main.go:59:3:59:3 | assignment to x | main.go:58:6:58:9 | cond |
| main.go:59:3:59:3 | skip | main.go:59:7:59:7 | 2 |
| main.go:59:7:59:7 | 2 | main.go:59:3:59:3 | assignment to x |
| main.go:61:2:61:10 | selection of Print | main.go:61:12:61:12 | x |
| main.go:61:2:61:13 | call to Print | main.go:56:1:80:1 | exit |
| main.go:61:2:61:13 | call to Print | main.go:63:2:63:2 | skip |
| main.go:61:12:61:12 | x | main.go:61:2:61:13 | call to Print |
| main.go:63:2:63:2 | assignment to y | main.go:64:6:64:6 | skip |
| main.go:63:2:63:2 | skip | main.go:63:7:63:7 | 1 |
| main.go:63:7:63:7 | 1 | main.go:63:2:63:2 | assignment to y |
| main.go:64:6:64:6 | assignment to i | main.go:65:6:65:9 | cond |
| main.go:64:6:64:6 | skip | main.go:64:11:64:11 | 0 |
| main.go:64:11:64:11 | 0 | main.go:64:6:64:6 | assignment to i |
| main.go:64:16:64:16 | i | main.go:64:16:64:18 | 1 |
| main.go:64:16:64:18 | 1 | main.go:64:16:64:18 | rhs of increment statement |
| main.go:64:16:64:18 | increment statement | main.go:65:6:65:9 | cond |
| main.go:64:16:64:18 | rhs of increment statement | main.go:64:16:64:18 | increment statement |
| main.go:65:6:65:9 | cond | main.go:65:6:65:11 | call to cond |
| main.go:65:6:65:11 | call to cond | main.go:56:1:80:1 | exit |
| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | call to cond is false |
| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | call to cond is true |
| main.go:65:6:65:11 | call to cond is false | main.go:68:3:68:3 | skip |
| main.go:65:6:65:11 | call to cond is true | main.go:66:4:66:8 | skip |
| main.go:66:4:66:8 | skip | main.go:70:2:70:10 | selection of Print |
| main.go:68:3:68:3 | assignment to y | main.go:64:16:64:16 | i |
| main.go:68:3:68:3 | skip | main.go:68:7:68:7 | 2 |
| main.go:68:7:68:7 | 2 | main.go:68:3:68:3 | assignment to y |
| main.go:70:2:70:10 | selection of Print | main.go:70:12:70:12 | y |
| main.go:70:2:70:13 | call to Print | main.go:56:1:80:1 | exit |
| main.go:70:2:70:13 | call to Print | main.go:72:2:72:2 | skip |
| main.go:70:12:70:12 | y | main.go:70:2:70:13 | call to Print |
| main.go:72:2:72:2 | assignment to z | main.go:73:6:73:6 | skip |
| main.go:72:2:72:2 | skip | main.go:72:7:72:7 | 1 |
| main.go:72:7:72:7 | 1 | main.go:72:2:72:2 | assignment to z |
| main.go:73:6:73:6 | assignment to i | main.go:74:3:74:3 | skip |
| main.go:73:6:73:6 | skip | main.go:73:11:73:11 | 0 |
| main.go:73:11:73:11 | 0 | main.go:73:6:73:6 | assignment to i |
| main.go:73:16:73:16 | i | main.go:73:16:73:18 | 1 |
| main.go:73:16:73:18 | 1 | main.go:73:16:73:18 | rhs of increment statement |
| main.go:73:16:73:18 | increment statement | main.go:74:3:74:3 | skip |
| main.go:73:16:73:18 | rhs of increment statement | main.go:73:16:73:18 | increment statement |
| main.go:74:3:74:3 | assignment to z | main.go:75:6:75:9 | cond |
| main.go:74:3:74:3 | skip | main.go:74:7:74:7 | 2 |
| main.go:74:7:74:7 | 2 | main.go:74:3:74:3 | assignment to z |
| main.go:56:1:64:1 | entry | main.go:56:11:56:18 | argument corresponding to selector |
| main.go:56:1:64:1 | function declaration | main.go:66:6:66:10 | skip |
| main.go:56:6:56:9 | skip | main.go:56:1:64:1 | function declaration |
| main.go:56:11:56:18 | argument corresponding to selector | main.go:56:11:56:18 | initialization of selector |
| main.go:56:11:56:18 | initialization of selector | main.go:56:26:56:31 | zero value for result |
| main.go:56:26:56:31 | implicit read of result | main.go:56:1:64:1 | exit |
| main.go:56:26:56:31 | initialization of result | main.go:57:2:57:7 | skip |
| main.go:56:26:56:31 | zero value for result | main.go:56:26:56:31 | initialization of result |
| main.go:57:2:57:7 | assignment to result | main.go:58:5:58:12 | selector |
| main.go:57:2:57:7 | skip | main.go:57:11:57:11 | 0 |
| main.go:57:11:57:11 | 0 | main.go:57:2:57:7 | assignment to result |
| main.go:58:5:58:12 | selector | main.go:58:17:58:17 | 1 |
| main.go:58:5:58:17 | ...==... | main.go:58:5:58:17 | ...==... is false |
| main.go:58:5:58:17 | ...==... | main.go:58:5:58:17 | ...==... is true |
| main.go:58:5:58:17 | ...==... is false | main.go:61:3:61:8 | skip |
| main.go:58:5:58:17 | ...==... is true | main.go:59:10:59:10 | 1 |
| main.go:58:17:58:17 | 1 | main.go:58:5:58:17 | ...==... |
| main.go:59:3:59:10 | return statement | main.go:56:26:56:31 | implicit read of result |
| main.go:59:10:59:10 | 1 | main.go:59:10:59:10 | implicit write of result |
| main.go:59:10:59:10 | implicit write of result | main.go:59:3:59:10 | return statement |
| main.go:61:3:61:8 | assignment to result | main.go:63:2:63:7 | return statement |
| main.go:61:3:61:8 | skip | main.go:61:12:61:12 | 2 |
| main.go:61:12:61:12 | 2 | main.go:61:3:61:8 | assignment to result |
| main.go:63:2:63:7 | return statement | main.go:56:26:56:31 | implicit read of result |
| main.go:66:1:90:1 | entry | main.go:67:6:67:6 | skip |
| main.go:66:1:90:1 | function declaration | main.go:92:6:92:13 | skip |
| main.go:66:6:66:10 | skip | main.go:66:1:90:1 | function declaration |
| main.go:67:6:67:6 | assignment to x | main.go:68:6:68:9 | cond |
| main.go:67:6:67:6 | skip | main.go:67:6:67:6 | zero value for x |
| main.go:67:6:67:6 | zero value for x | main.go:67:6:67:6 | assignment to x |
| main.go:68:6:68:9 | cond | main.go:68:6:68:11 | call to cond |
| main.go:68:6:68:11 | call to cond | main.go:66:1:90:1 | exit |
| main.go:68:6:68:11 | call to cond | main.go:68:6:68:11 | call to cond is false |
| main.go:68:6:68:11 | call to cond | main.go:68:6:68:11 | call to cond is true |
| main.go:68:6:68:11 | call to cond is false | main.go:71:2:71:10 | selection of Print |
| main.go:68:6:68:11 | call to cond is true | main.go:69:3:69:3 | skip |
| main.go:69:3:69:3 | assignment to x | main.go:68:6:68:9 | cond |
| main.go:69:3:69:3 | skip | main.go:69:7:69:7 | 2 |
| main.go:69:7:69:7 | 2 | main.go:69:3:69:3 | assignment to x |
| main.go:71:2:71:10 | selection of Print | main.go:71:12:71:12 | x |
| main.go:71:2:71:13 | call to Print | main.go:66:1:90:1 | exit |
| main.go:71:2:71:13 | call to Print | main.go:73:2:73:2 | skip |
| main.go:71:12:71:12 | x | main.go:71:2:71:13 | call to Print |
| main.go:73:2:73:2 | assignment to y | main.go:74:6:74:6 | skip |
| main.go:73:2:73:2 | skip | main.go:73:7:73:7 | 1 |
| main.go:73:7:73:7 | 1 | main.go:73:2:73:2 | assignment to y |
| main.go:74:6:74:6 | assignment to i | main.go:75:6:75:9 | cond |
| main.go:74:6:74:6 | skip | main.go:74:11:74:11 | 0 |
| main.go:74:11:74:11 | 0 | main.go:74:6:74:6 | assignment to i |
| main.go:74:16:74:16 | i | main.go:74:16:74:18 | 1 |
| main.go:74:16:74:18 | 1 | main.go:74:16:74:18 | rhs of increment statement |
| main.go:74:16:74:18 | increment statement | main.go:75:6:75:9 | cond |
| main.go:74:16:74:18 | rhs of increment statement | main.go:74:16:74:18 | increment statement |
| main.go:75:6:75:9 | cond | main.go:75:6:75:11 | call to cond |
| main.go:75:6:75:11 | call to cond | main.go:56:1:80:1 | exit |
| main.go:75:6:75:11 | call to cond | main.go:66:1:90:1 | exit |
| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | call to cond is false |
| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | call to cond is true |
| main.go:75:6:75:11 | call to cond is false | main.go:73:16:73:16 | i |
| main.go:75:6:75:11 | call to cond is false | main.go:78:3:78:3 | skip |
| main.go:75:6:75:11 | call to cond is true | main.go:76:4:76:8 | skip |
| main.go:76:4:76:8 | skip | main.go:79:2:79:10 | selection of Print |
| main.go:79:2:79:10 | selection of Print | main.go:79:12:79:12 | z |
| main.go:79:2:79:13 | call to Print | main.go:56:1:80:1 | exit |
| main.go:79:12:79:12 | z | main.go:79:2:79:13 | call to Print |
| main.go:82:1:86:1 | entry | main.go:82:18:82:18 | zero value for a |
| main.go:82:1:86:1 | function declaration | main.go:88:6:88:23 | skip |
| main.go:82:6:82:13 | skip | main.go:82:1:86:1 | function declaration |
| main.go:82:18:82:18 | implicit read of a | main.go:82:25:82:25 | implicit read of b |
| main.go:82:18:82:18 | initialization of a | main.go:82:25:82:25 | zero value for b |
| main.go:82:18:82:18 | zero value for a | main.go:82:18:82:18 | initialization of a |
| main.go:82:25:82:25 | implicit read of b | main.go:82:1:86:1 | exit |
| main.go:82:25:82:25 | initialization of b | main.go:83:2:83:2 | skip |
| main.go:82:25:82:25 | zero value for b | main.go:82:25:82:25 | initialization of b |
| main.go:83:2:83:2 | assignment to x | main.go:84:2:84:2 | skip |
| main.go:83:2:83:2 | skip | main.go:83:7:83:8 | 23 |
| main.go:83:7:83:8 | 23 | main.go:83:2:83:2 | assignment to x |
| main.go:84:2:84:2 | assignment to x | main.go:84:5:84:5 | assignment to a |
| main.go:84:2:84:2 | skip | main.go:84:5:84:5 | skip |
| main.go:84:5:84:5 | assignment to a | main.go:85:2:85:7 | return statement |
| main.go:84:5:84:5 | skip | main.go:84:9:84:9 | x |
| main.go:84:9:84:9 | x | main.go:84:11:84:12 | 19 |
| main.go:84:9:84:12 | ...+... | main.go:84:15:84:15 | x |
| main.go:84:11:84:12 | 19 | main.go:84:9:84:12 | ...+... |
| main.go:84:15:84:15 | x | main.go:84:2:84:2 | assignment to x |
| main.go:85:2:85:7 | return statement | main.go:82:18:82:18 | implicit read of a |
| main.go:88:1:96:1 | entry | main.go:88:25:88:25 | argument corresponding to x |
| main.go:88:1:96:1 | function declaration | main.go:0:0:0:0 | exit |
| main.go:88:6:88:23 | skip | main.go:88:1:96:1 | function declaration |
| main.go:88:25:88:25 | argument corresponding to x | main.go:88:25:88:25 | initialization of x |
| main.go:88:25:88:25 | initialization of x | main.go:89:2:89:2 | skip |
| main.go:89:2:89:2 | assignment to a | main.go:89:5:89:5 | assignment to b |
| main.go:89:2:89:2 | skip | main.go:89:5:89:5 | skip |
| main.go:89:5:89:5 | assignment to b | main.go:90:5:90:8 | cond |
| main.go:89:5:89:5 | skip | main.go:89:10:89:10 | x |
| main.go:89:10:89:10 | x | main.go:89:13:89:13 | 0 |
| main.go:89:13:89:13 | 0 | main.go:89:2:89:2 | assignment to a |
| main.go:90:5:90:8 | cond | main.go:90:5:90:10 | call to cond |
| main.go:90:5:90:10 | call to cond | main.go:88:1:96:1 | exit |
| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | call to cond is false |
| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | call to cond is true |
| main.go:90:5:90:10 | call to cond is false | main.go:93:3:93:3 | skip |
| main.go:90:5:90:10 | call to cond is true | main.go:91:3:91:3 | skip |
| main.go:91:3:91:3 | assignment to a | main.go:95:9:95:9 | a |
| main.go:91:3:91:3 | skip | main.go:91:6:91:6 | skip |
| main.go:91:6:91:6 | skip | main.go:91:10:91:10 | b |
| main.go:91:10:91:10 | b | main.go:91:13:91:13 | a |
| main.go:91:13:91:13 | a | main.go:91:3:91:3 | assignment to a |
| main.go:93:3:93:3 | skip | main.go:93:6:93:6 | skip |
| main.go:93:6:93:6 | assignment to b | main.go:95:9:95:9 | a |
| main.go:93:6:93:6 | skip | main.go:93:10:93:10 | b |
| main.go:93:10:93:10 | b | main.go:93:13:93:13 | a |
| main.go:93:13:93:13 | a | main.go:93:6:93:6 | assignment to b |
| main.go:95:2:95:12 | return statement | main.go:88:1:96:1 | exit |
| main.go:95:9:95:9 | a | main.go:95:12:95:12 | b |
| main.go:95:12:95:12 | b | main.go:95:2:95:12 | return statement |
| main.go:76:4:76:8 | skip | main.go:80:2:80:10 | selection of Print |
| main.go:78:3:78:3 | assignment to y | main.go:74:16:74:16 | i |
| main.go:78:3:78:3 | skip | main.go:78:7:78:7 | 2 |
| main.go:78:7:78:7 | 2 | main.go:78:3:78:3 | assignment to y |
| main.go:80:2:80:10 | selection of Print | main.go:80:12:80:12 | y |
| main.go:80:2:80:13 | call to Print | main.go:66:1:90:1 | exit |
| main.go:80:2:80:13 | call to Print | main.go:82:2:82:2 | skip |
| main.go:80:12:80:12 | y | main.go:80:2:80:13 | call to Print |
| main.go:82:2:82:2 | assignment to z | main.go:83:6:83:6 | skip |
| main.go:82:2:82:2 | skip | main.go:82:7:82:7 | 1 |
| main.go:82:7:82:7 | 1 | main.go:82:2:82:2 | assignment to z |
| main.go:83:6:83:6 | assignment to i | main.go:84:3:84:3 | skip |
| main.go:83:6:83:6 | skip | main.go:83:11:83:11 | 0 |
| main.go:83:11:83:11 | 0 | main.go:83:6:83:6 | assignment to i |
| main.go:83:16:83:16 | i | main.go:83:16:83:18 | 1 |
| main.go:83:16:83:18 | 1 | main.go:83:16:83:18 | rhs of increment statement |
| main.go:83:16:83:18 | increment statement | main.go:84:3:84:3 | skip |
| main.go:83:16:83:18 | rhs of increment statement | main.go:83:16:83:18 | increment statement |
| main.go:84:3:84:3 | assignment to z | main.go:85:6:85:9 | cond |
| main.go:84:3:84:3 | skip | main.go:84:7:84:7 | 2 |
| main.go:84:7:84:7 | 2 | main.go:84:3:84:3 | assignment to z |
| main.go:85:6:85:9 | cond | main.go:85:6:85:11 | call to cond |
| main.go:85:6:85:11 | call to cond | main.go:66:1:90:1 | exit |
| main.go:85:6:85:11 | call to cond | main.go:85:6:85:11 | call to cond is false |
| main.go:85:6:85:11 | call to cond | main.go:85:6:85:11 | call to cond is true |
| main.go:85:6:85:11 | call to cond is false | main.go:83:16:83:16 | i |
| main.go:85:6:85:11 | call to cond is true | main.go:86:4:86:8 | skip |
| main.go:86:4:86:8 | skip | main.go:89:2:89:10 | selection of Print |
| main.go:89:2:89:10 | selection of Print | main.go:89:12:89:12 | z |
| main.go:89:2:89:13 | call to Print | main.go:66:1:90:1 | exit |
| main.go:89:12:89:12 | z | main.go:89:2:89:13 | call to Print |
| main.go:92:1:96:1 | entry | main.go:92:18:92:18 | zero value for a |
| main.go:92:1:96:1 | function declaration | main.go:98:6:98:23 | skip |
| main.go:92:6:92:13 | skip | main.go:92:1:96:1 | function declaration |
| main.go:92:18:92:18 | implicit read of a | main.go:92:25:92:25 | implicit read of b |
| main.go:92:18:92:18 | initialization of a | main.go:92:25:92:25 | zero value for b |
| main.go:92:18:92:18 | zero value for a | main.go:92:18:92:18 | initialization of a |
| main.go:92:25:92:25 | implicit read of b | main.go:92:1:96:1 | exit |
| main.go:92:25:92:25 | initialization of b | main.go:93:2:93:2 | skip |
| main.go:92:25:92:25 | zero value for b | main.go:92:25:92:25 | initialization of b |
| main.go:93:2:93:2 | assignment to x | main.go:94:2:94:2 | skip |
| main.go:93:2:93:2 | skip | main.go:93:7:93:8 | 23 |
| main.go:93:7:93:8 | 23 | main.go:93:2:93:2 | assignment to x |
| main.go:94:2:94:2 | assignment to x | main.go:94:5:94:5 | assignment to a |
| main.go:94:2:94:2 | skip | main.go:94:5:94:5 | skip |
| main.go:94:5:94:5 | assignment to a | main.go:95:2:95:7 | return statement |
| main.go:94:5:94:5 | skip | main.go:94:9:94:9 | x |
| main.go:94:9:94:9 | x | main.go:94:11:94:12 | 19 |
| main.go:94:9:94:12 | ...+... | main.go:94:15:94:15 | x |
| main.go:94:11:94:12 | 19 | main.go:94:9:94:12 | ...+... |
| main.go:94:15:94:15 | x | main.go:94:2:94:2 | assignment to x |
| main.go:95:2:95:7 | return statement | main.go:92:18:92:18 | implicit read of a |
| main.go:98:1:106:1 | entry | main.go:98:25:98:25 | argument corresponding to x |
| main.go:98:1:106:1 | function declaration | main.go:0:0:0:0 | exit |
| main.go:98:6:98:23 | skip | main.go:98:1:106:1 | function declaration |
| main.go:98:25:98:25 | argument corresponding to x | main.go:98:25:98:25 | initialization of x |
| main.go:98:25:98:25 | initialization of x | main.go:99:2:99:2 | skip |
| main.go:99:2:99:2 | assignment to a | main.go:99:5:99:5 | assignment to b |
| main.go:99:2:99:2 | skip | main.go:99:5:99:5 | skip |
| main.go:99:5:99:5 | assignment to b | main.go:100:5:100:8 | cond |
| main.go:99:5:99:5 | skip | main.go:99:10:99:10 | x |
| main.go:99:10:99:10 | x | main.go:99:13:99:13 | 0 |
| main.go:99:13:99:13 | 0 | main.go:99:2:99:2 | assignment to a |
| main.go:100:5:100:8 | cond | main.go:100:5:100:10 | call to cond |
| main.go:100:5:100:10 | call to cond | main.go:98:1:106:1 | exit |
| main.go:100:5:100:10 | call to cond | main.go:100:5:100:10 | call to cond is false |
| main.go:100:5:100:10 | call to cond | main.go:100:5:100:10 | call to cond is true |
| main.go:100:5:100:10 | call to cond is false | main.go:103:3:103:3 | skip |
| main.go:100:5:100:10 | call to cond is true | main.go:101:3:101:3 | skip |
| main.go:101:3:101:3 | assignment to a | main.go:105:9:105:9 | a |
| main.go:101:3:101:3 | skip | main.go:101:6:101:6 | skip |
| main.go:101:6:101:6 | skip | main.go:101:10:101:10 | b |
| main.go:101:10:101:10 | b | main.go:101:13:101:13 | a |
| main.go:101:13:101:13 | a | main.go:101:3:101:3 | assignment to a |
| main.go:103:3:103:3 | skip | main.go:103:6:103:6 | skip |
| main.go:103:6:103:6 | assignment to b | main.go:105:9:105:9 | a |
| main.go:103:6:103:6 | skip | main.go:103:10:103:10 | b |
| main.go:103:10:103:10 | b | main.go:103:13:103:13 | a |
| main.go:103:13:103:13 | a | main.go:103:6:103:6 | assignment to b |
| main.go:105:2:105:12 | return statement | main.go:98:1:106:1 | exit |
| main.go:105:9:105:9 | a | main.go:105:12:105:12 | b |
| main.go:105:12:105:12 | b | main.go:105:2:105:12 | return statement |
| noretfunctions.go:0:0:0:0 | entry | noretfunctions.go:3:1:6:1 | skip |
| noretfunctions.go:3:1:6:1 | skip | noretfunctions.go:8:6:8:12 | skip |
| noretfunctions.go:8:1:10:1 | entry | noretfunctions.go:9:2:9:8 | selection of Exit |

View File

@@ -53,6 +53,16 @@ func baz2() (result int) {
return
}
func baz3(selector int) (result int) {
result = 0
if selector == 1 {
return 1
} else {
result = 2
}
return
}
func loops() {
var x int
for cond() {

View File

@@ -2,3 +2,5 @@
| main.go:7:19:7:23 | ...+... | + | main.go:7:19:7:19 | y | main.go:7:23:7:23 | z |
| main.go:10:14:10:18 | ...+... | + | main.go:10:14:10:14 | x | main.go:10:18:10:18 | y |
| main.go:17:2:17:13 | ... += ... | + | main.go:17:2:17:6 | index expression | main.go:17:11:17:13 | "!" |
| resultParameters.go:4:5:4:17 | ...==... | == | resultParameters.go:4:5:4:12 | selector | resultParameters.go:4:17:4:17 | 0 |
| resultParameters.go:23:5:23:17 | ...==... | == | resultParameters.go:23:5:23:12 | selector | resultParameters.go:23:17:23:17 | 1 |

View File

@@ -0,0 +1,8 @@
| main.go:21:9:21:10 | 23 | Result node with index 0 |
| main.go:21:13:21:14 | 42 | Result node with index 1 |
| resultParameters.go:5:10:5:10 | 0 | Result node with index 0 |
| resultParameters.go:9:10:9:10 | 1 | Result node with index 0 |
| resultParameters.go:11:10:11:10 | 2 | Result node with index 0 |
| resultParameters.go:13:9:13:9 | 3 | Result node with index 0 |
| resultParameters.go:16:26:16:26 | implicit read of r | Result node with index 0 |
| resultParameters.go:21:38:21:38 | implicit read of r | Result node with index 0 |

View File

@@ -0,0 +1,9 @@
/**
* @kind problem
* @id result-node
*/
import go
from DataFlow::ResultNode r
select r, "Result node with index " + r.getIndex()

View File

@@ -0,0 +1,2 @@
query: ResultNode.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -18,5 +18,5 @@ func f() {
}
func test() (int, int) {
return 23, 42
return 23, 42 // $ Alert[result-node]
}

View File

@@ -0,0 +1,27 @@
package main
func multipleReturns(selector int) int {
if selector == 0 {
return 0 // $ Alert[result-node]
}
switch selector {
case 1:
return 1 // $ Alert[result-node]
case 2:
return 2 // $ Alert[result-node]
}
return 3 // $ Alert[result-node]
}
func resultParameter1() (r int) { // $ Alert[result-node] // implicit reads of result parameters are located at the result parameter declaration
r = 0
return
}
func resultParameter2(selector int) (r int) { // $ Alert[result-node] // implicit reads of result parameters are located at the result parameter declaration
r = 0
if selector == 1 {
return 1
}
return
}

View File

@@ -5,9 +5,9 @@
| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile |
| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile |
| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile |
| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile |
| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile |
| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile |
| tests.go:126:9:126:9 | f | tests.go:124:5:124:78 | ... := ...[0] | tests.go:126:9:126:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:124:15:124:78 | call to OpenFile | call to OpenFile |
| tests.go:145:3:145:3 | f | tests.go:141:5:141:78 | ... := ...[0] | tests.go:145:3:145:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:141:15:141:78 | call to OpenFile | call to OpenFile |
| tests.go:166:8:166:8 | f | tests.go:162:2:162:74 | ... := ...[0] | tests.go:166:8:166:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:162:12:162:74 | call to OpenFile | call to OpenFile |
edges
| tests.go:9:24:9:24 | definition of f | tests.go:10:8:10:8 | f | provenance | |
| tests.go:13:32:13:32 | definition of f | tests.go:14:13:16:2 | capture variable f | provenance | |
@@ -22,9 +22,9 @@ edges
| tests.go:48:29:48:29 | f | tests.go:13:32:13:32 | definition of f | provenance | |
| tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 |
| tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 |
| tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 |
| tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 |
| tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 |
| tests.go:124:5:124:78 | ... := ...[0] | tests.go:126:9:126:9 | f | provenance | Src:MaD:1 |
| tests.go:141:5:141:78 | ... := ...[0] | tests.go:145:3:145:3 | f | provenance | Src:MaD:1 |
| tests.go:162:2:162:74 | ... := ...[0] | tests.go:166:8:166:8 | f | provenance | Src:MaD:1 |
models
| 1 | Source: os; ; false; OpenFile; ; ; ReturnValue[0]; file; manual |
nodes
@@ -43,10 +43,10 @@ nodes
| tests.go:57:3:57:3 | f | semmle.label | f |
| tests.go:67:5:67:76 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:69:3:69:3 | f | semmle.label | f |
| tests.go:109:5:109:78 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:111:9:111:9 | f | semmle.label | f |
| tests.go:126:5:126:78 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:130:3:130:3 | f | semmle.label | f |
| tests.go:147:2:147:74 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:151:8:151:8 | f | semmle.label | f |
| tests.go:124:5:124:78 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:126:9:126:9 | f | semmle.label | f |
| tests.go:141:5:141:78 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:145:3:145:3 | f | semmle.label | f |
| tests.go:162:2:162:74 | ... := ...[0] | semmle.label | ... := ...[0] |
| tests.go:166:8:166:8 | f | semmle.label | f |
subpaths

View File

@@ -104,6 +104,21 @@ func deferredCloseWithSync() {
}
}
func deferredCloseWithSync2() {
// open file for writing
if f, err := os.OpenFile("foo.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666); err != nil {
// a call to `Close` is deferred, but we have a call to `Sync` later which
// precedes the call to `Close` during execution
defer f.Close()
if err := f.Sync(); err != nil {
log.Fatal(err)
}
}
var a int
_ = a
}
func deferredCloseWithSyncEarlyReturn(n int) {
// open file for writing
if f, err := os.OpenFile("foo.txt", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666); err != nil { // $ Source