Merge branch 'main' into post-release-prep/codeql-cli-2.8.0

This commit is contained in:
Tom Hvitved
2022-02-09 09:40:33 +01:00
committed by GitHub
767 changed files with 220815 additions and 38005 deletions

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Nullable>enable</Nullable>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
<ApplicationIcon />

View File

@@ -1,4 +1,6 @@
name: codeql/cpp-examples
version: 0.0.2
groups:
- cpp
- examples
dependencies:
codeql/cpp-all: "*"

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/world-writable-file-creation` query now only detects `open` and `openat` calls with the `O_CREAT` or `O_TMPFILE` flag.

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `cpp/open-call-with-mode-argument`, to detect when `open` or `openat` is called with the `O_CREAT` or `O_TMPFILE` flag but when the `mode` argument is omitted.

View File

@@ -11,10 +11,10 @@ import cpp
*/
bindingset[input]
int parseOctal(string input) {
input.charAt(0) = "0" and
input.regexpMatch("0[0-7]+") and
result =
strictsum(int ix |
ix in [0 .. input.length()]
ix in [1 .. input.length()]
|
8.pow(input.length() - (ix + 1)) * input.charAt(ix).toInt()
)

View File

@@ -1290,7 +1290,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** Content tagged with the type of a containing object. */
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;

View File

@@ -592,12 +592,14 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
/**
* Holds if data can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
/**

View File

@@ -124,12 +124,14 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
/**
* Holds if taint can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprTaint(Expr e1, Expr e2) {
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
}

View File

@@ -1,3 +1,8 @@
/**
* An IR taint tracking library that uses an IR DataFlow configuration to track
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
*/
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow

View File

@@ -1290,7 +1290,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** Content tagged with the type of a containing object. */
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;

View File

@@ -1032,12 +1032,14 @@ SideEffectInstruction getSideEffectFor(CallInstruction call, int argument) {
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
/**
* Holds if data can flow from `i1` to `i2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localInstructionFlow(Instruction e1, Instruction e2) {
localFlow(instructionNode(e1), instructionNode(e2))
}
@@ -1046,6 +1048,7 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
* Holds if data can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
private newtype TContent =

View File

@@ -121,12 +121,14 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
pragma[inline]
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
/**
* Holds if taint can flow from `i1` to `i2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localInstructionTaint(Instruction i1, Instruction i2) {
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
}
@@ -135,6 +137,7 @@ predicate localInstructionTaint(Instruction i1, Instruction i2) {
* Holds if taint can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
*/
pragma[inline]
predicate localExprTaint(Expr e1, Expr e2) {
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
}

View File

@@ -567,6 +567,13 @@ newtype TTranslatedElement =
} or
// The initialization of a base class from within a constructor.
TTranslatedConstructorBaseInit(ConstructorBaseInit init) { not ignoreExpr(init) } or
// Workaround for a case where no base constructor is generated but a targetless base
// constructor call is present.
TTranslatedConstructorBareInit(ConstructorInit init) {
not ignoreExpr(init) and
not init instanceof ConstructorBaseInit and
not init instanceof ConstructorFieldInit
} or
// The destruction of a base class from within a destructor.
TTranslatedDestructorBaseDestruction(DestructorBaseDestruction destruction) {
not ignoreExpr(destruction)

View File

@@ -573,6 +573,11 @@ class TranslatedConstructorInitList extends TranslatedElement, InitializationCon
baseInit = func.(Constructor).getInitializer(id) and
result = getTranslatedConstructorBaseInit(baseInit)
)
or
exists(ConstructorInit bareInit |
bareInit = func.(Constructor).getInitializer(id) and
result = getTranslatedConstructorBareInit(bareInit)
)
}
override Instruction getFirstInstruction() {

View File

@@ -917,3 +917,36 @@ class TranslatedDestructorBaseDestruction extends TranslatedBaseStructorCall,
final override string toString() { result = "destroy base: " + call.toString() }
}
/**
* A constructor base init call where no base constructor has been generated.
*
* Workaround for an extractor issue.
*/
class TranslatedConstructorBareInit extends TranslatedElement, TTranslatedConstructorBareInit {
ConstructorInit init;
TranslatedConstructorBareInit() { this = TTranslatedConstructorBareInit(init) }
override Locatable getAST() { result = init }
final override string toString() { result = "construct base (no constructor)" }
override Instruction getFirstInstruction() { result = getParent().getChildSuccessor(this) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override TranslatedElement getChild(int id) { none() }
override Function getFunction() { result = getParent().getFunction() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildSuccessor(TranslatedElement child) { none() }
}
TranslatedConstructorBareInit getTranslatedConstructorBareInit(ConstructorInit init) {
result.getAST() = init
}

View File

@@ -11,15 +11,14 @@ import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.FlowSource
/**
* The standard functions `gets` and `fgets`.
* The standard functions `fgets` and `fgetws`.
*/
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction, RemoteFlowSourceFunction {
GetsFunction() {
// gets(str)
FgetsFunction() {
// fgets(str, num, stream)
// fgetws(wstr, num, stream)
this.hasGlobalOrStdOrBslName(["gets", "fgets", "fgetws"])
this.hasGlobalOrStdOrBslName(["fgets", "fgetws"])
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
@@ -51,20 +50,61 @@ private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunctio
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
output.isParameterDeref(0) and
description = "String read by " + this.getName()
or
output.isReturnValue() and
description = "String read by " + this.getName()
}
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
not this.hasName("gets") and
bufParam = 0 and
countParam = 1
}
override predicate hasArrayWithUnknownSize(int bufParam) {
this.hasName("gets") and
bufParam = 0
}
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
override predicate hasSocketInput(FunctionInput input) { input.isParameter(2) }
override predicate hasSocketInput(FunctionInput input) { input.isParameterDeref(2) }
}
/**
* The standard functions `gets`.
*/
private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunction,
SideEffectFunction, LocalFlowSourceFunction {
GetsFunction() {
// gets(str)
this.hasGlobalOrStdOrBslName("gets")
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate parameterNeverEscapes(int index) { none() }
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = true
}
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
output.isParameterDeref(0) and
description = "String read by " + this.getName()
or
output.isReturnValue() and
description = "String read by " + this.getName()
}
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 0 }
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}

View File

@@ -1,7 +1,10 @@
/*
* Support for tracking tainted data through the program.
* Support for tracking tainted data through the program. This is an alias for
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
* compatibility.
*
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` when designing new queries.
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` or
* `semmle.code.cpp.ir.dataflow.TaintTracking` when designing new queries.
*/
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking

View File

@@ -6,122 +6,22 @@
*/
class Person extends string {
Person() {
this = "Ronil" or
this = "Dina" or
this = "Ravi" or
this = "Bruce" or
this = "Jo" or
this = "Aida" or
this = "Esme" or
this = "Charlie" or
this = "Fred" or
this = "Meera" or
this = "Maya" or
this = "Chad" or
this = "Tiana" or
this = "Laura" or
this = "George" or
this = "Will" or
this = "Mary" or
this = "Almira" or
this = "Susannah" or
this = "Rhoda" or
this = "Cynthia" or
this = "Eunice" or
this = "Olive" or
this = "Virginia" or
this = "Angeline" or
this = "Helen" or
this = "Cornelia" or
this = "Harriet" or
this = "Mahala" or
this = "Abby" or
this = "Margaret" or
this = "Deb" or
this = "Minerva" or
this = "Severus" or
this = "Lavina" or
this = "Adeline" or
this = "Cath" or
this = "Elisa" or
this = "Lucretia" or
this = "Anne" or
this = "Eleanor" or
this = "Joanna" or
this = "Adam" or
this = "Agnes" or
this = "Rosanna" or
this = "Clara" or
this = "Melissa" or
this = "Amy" or
this = "Isabel" or
this = "Jemima" or
this = "Cordelia" or
this = "Melinda" or
this = "Delila" or
this = "Jeremiah" or
this = "Elijah" or
this = "Hester" or
this = "Walter" or
this = "Oliver" or
this = "Hugh" or
this = "Aaron" or
this = "Reuben" or
this = "Eli" or
this = "Amos" or
this = "Augustus" or
this = "Theodore" or
this = "Ira" or
this = "Timothy" or
this = "Cyrus" or
this = "Horace" or
this = "Simon" or
this = "Asa" or
this = "Frank" or
this = "Nelson" or
this = "Leonard" or
this = "Harrison" or
this = "Anthony" or
this = "Louis" or
this = "Milton" or
this = "Noah" or
this = "Cornelius" or
this = "Abdul" or
this = "Warren" or
this = "Harvey" or
this = "Dennis" or
this = "Wesley" or
this = "Sylvester" or
this = "Gilbert" or
this = "Sullivan" or
this = "Edmund" or
this = "Wilson" or
this = "Perry" or
this = "Matthew" or
this = "Simba" or
this = "Nala" or
this = "Rafiki" or
this = "Shenzi" or
this = "Ernest" or
this = "Gertrude" or
this = "Oscar" or
this = "Lilian" or
this = "Raymond" or
this = "Elgar" or
this = "Elmer" or
this = "Herbert" or
this = "Maude" or
this = "Mae" or
this = "Otto" or
this = "Edwin" or
this = "Ophelia" or
this = "Parsley" or
this = "Sage" or
this = "Rosemary" or
this = "Thyme" or
this = "Garfunkel" or
this = "King Basil" or
this = "Stephen"
this =
[
"Ronil", "Dina", "Ravi", "Bruce", "Jo", "Aida", "Esme", "Charlie", "Fred", "Meera", "Maya",
"Chad", "Tiana", "Laura", "George", "Will", "Mary", "Almira", "Susannah", "Rhoda",
"Cynthia", "Eunice", "Olive", "Virginia", "Angeline", "Helen", "Cornelia", "Harriet",
"Mahala", "Abby", "Margaret", "Deb", "Minerva", "Severus", "Lavina", "Adeline", "Cath",
"Elisa", "Lucretia", "Anne", "Eleanor", "Joanna", "Adam", "Agnes", "Rosanna", "Clara",
"Melissa", "Amy", "Isabel", "Jemima", "Cordelia", "Melinda", "Delila", "Jeremiah", "Elijah",
"Hester", "Walter", "Oliver", "Hugh", "Aaron", "Reuben", "Eli", "Amos", "Augustus",
"Theodore", "Ira", "Timothy", "Cyrus", "Horace", "Simon", "Asa", "Frank", "Nelson",
"Leonard", "Harrison", "Anthony", "Louis", "Milton", "Noah", "Cornelius", "Abdul", "Warren",
"Harvey", "Dennis", "Wesley", "Sylvester", "Gilbert", "Sullivan", "Edmund", "Wilson",
"Perry", "Matthew", "Simba", "Nala", "Rafiki", "Shenzi", "Ernest", "Gertrude", "Oscar",
"Lilian", "Raymond", "Elgar", "Elmer", "Herbert", "Maude", "Mae", "Otto", "Edwin",
"Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel", "King Basil", "Stephen"
]
}
/** Gets the hair color of the person. If the person is bald, there is no result. */
@@ -936,25 +836,12 @@ class Person extends string {
/** Holds if the person is deceased. */
predicate isDeceased() {
this = "Ernest" or
this = "Gertrude" or
this = "Oscar" or
this = "Lilian" or
this = "Edwin" or
this = "Raymond" or
this = "Elgar" or
this = "Elmer" or
this = "Herbert" or
this = "Maude" or
this = "Mae" or
this = "Otto" or
this = "Ophelia" or
this = "Parsley" or
this = "Sage" or
this = "Rosemary" or
this = "Thyme" or
this = "Garfunkel" or
this = "King Basil"
this =
[
"Ernest", "Gertrude", "Oscar", "Lilian", "Edwin", "Raymond", "Elgar", "Elmer", "Herbert",
"Maude", "Mae", "Otto", "Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel",
"King Basil"
]
}
/** Gets a parent of the person (alive or deceased). */
@@ -1195,12 +1082,7 @@ class Person extends string {
}
/** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */
predicate isAllowedIn(string region) {
region = "north" or
region = "south" or
region = "east" or
region = "west"
}
predicate isAllowedIn(string region) { region = ["north", "south", "east", "west"] }
}
/** Returns a parent of the person. */

View File

@@ -14,6 +14,9 @@
*/
import cpp
// We don't actually use the global value numbering library in this query, but without it we end up
// recomputing the IR.
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow

View File

@@ -12,23 +12,33 @@
*/
import cpp
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.security.BufferWrite as BufferWrite
import semmle.code.cpp.security.SensitiveExprs
import TaintedWithPath
import semmle.code.cpp.security.FlowSources
import semmle.code.cpp.ir.dataflow.TaintTracking
import DataFlow::PathGraph
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { exists(BufferWrite w | w.getASource() = tainted) }
/**
* Taint flow from user input to a buffer write.
*/
class ToBufferConfiguration extends TaintTracking::Configuration {
ToBufferConfiguration() { this = "ToBufferConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(BufferWrite::BufferWrite w | w.getASource() = sink.asExpr())
}
}
from
BufferWrite w, Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode,
string taintCause, SensitiveExpr dest
ToBufferConfiguration config, BufferWrite::BufferWrite w, DataFlow::PathNode sourceNode,
DataFlow::PathNode sinkNode, FlowSource source, SensitiveExpr dest
where
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) and
w.getASource() = taintedArg and
config.hasFlowPath(sourceNode, sinkNode) and
sourceNode.getNode() = source and
w.getASource() = sinkNode.getNode().asExpr() and
dest = w.getDest()
select w, sourceNode, sinkNode,
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@",
taintSource, "user input (" + taintCause + ")"
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@", source,
"user input (" + source.getSourceType() + ")"

View File

@@ -65,6 +65,7 @@ where
midNode.getNode().asExpr() = mid and
mid = w.getASource() and
dest = w.getDest() and
not dest.(VariableAccess).getTarget().getName() = ["stdin", "stdout", "stderr"] and // exclude calls with standard streams
not isFileName(globalValueNumber(source)) and // file names are not passwords
not exists(string convChar | convChar = w.getSourceConvChar(mid) | not convChar = ["s", "S"]) // ignore things written with other conversion characters
select w, sourceNode, midNode,

View File

@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision medium
* @precision high
* @id cpp/cleartext-transmission
* @tags security
* external/cwe/cwe-319
@@ -14,8 +14,8 @@
import cpp
import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.commons.File
import DataFlow::PathGraph
/**
@@ -27,6 +27,7 @@ class SensitiveNode extends DataFlow::Node {
this.asExpr() = any(SensitiveVariable sv).getInitializer().getExpr() or
this.asExpr().(VariableAccess).getTarget() =
any(SensitiveVariable sv).(GlobalOrNamespaceVariable) or
this.asExpr().(VariableAccess).getTarget() = any(SensitiveVariable v | v instanceof Field) or
this.asUninitialized() instanceof SensitiveVariable or
this.asParameter() instanceof SensitiveVariable or
this.asExpr().(FunctionCall).getTarget() instanceof SensitiveFunction
@@ -58,7 +59,10 @@ class Send extends SendRecv instanceof RemoteFlowSinkFunction {
call.getTarget() = this and
exists(FunctionInput input, int arg |
super.hasSocketInput(input) and
input.isParameter(arg) and
(
input.isParameter(arg) or
input.isParameterDeref(arg)
) and
result = call.getArgument(arg)
)
}
@@ -81,7 +85,10 @@ class Recv extends SendRecv instanceof RemoteFlowSourceFunction {
call.getTarget() = this and
exists(FunctionInput input, int arg |
super.hasSocketInput(input) and
input.isParameter(arg) and
(
input.isParameter(arg) or
input.isParameterDeref(arg)
) and
result = call.getArgument(arg)
)
}
@@ -114,24 +121,32 @@ abstract class NetworkSendRecv extends FunctionCall {
NetworkSendRecv() {
this.getTarget() = target and
// exclude calls based on the socket...
not exists(GVN g |
g = globalValueNumber(target.getSocketExpr(this)) and
not exists(DataFlow::Node src, DataFlow::Node dest |
DataFlow::localFlow(src, dest) and
dest.asExpr() = target.getSocketExpr(this) and
(
// literal constant
globalValueNumber(any(Literal l)) = g
src.asExpr() instanceof Literal
or
// variable (such as a global) initialized to a literal constant
exists(Variable v |
v.getInitializer().getExpr() instanceof Literal and
g = globalValueNumber(v.getAnAccess())
src.asExpr() = v.getAnAccess()
)
or
// result of a function call with literal inputs (likely constant)
forex(Expr arg | arg = src.asExpr().(FunctionCall).getAnArgument() | arg instanceof Literal)
or
// variable called `stdin`, `stdout` or `stderr`
src.asExpr().(VariableAccess).getTarget().getName() = ["stdin", "stdout", "stderr"]
or
// open of `"/dev/tty"`
exists(FunctionCall fc |
forex(Expr arg | arg = fc.getAnArgument() | arg instanceof Literal) and
g = globalValueNumber(fc)
fopenCall(fc) and
fc.getAnArgument().getValue() = "/dev/tty" and
src.asExpr() = fc
)
// (this is far from exhaustive)
// (this is not exhaustive)
)
)
}

View File

@@ -12,17 +12,16 @@
import cpp
import FilePermissions
import semmle.code.cpp.commons.unix.Constants
predicate worldWritableCreation(FileCreationExpr fc, int mode) {
mode = localUmask(fc).mask(fc.getMode()) and
sets(mode, s_iwoth())
setsAnyBits(mode, UnixConstants::s_iwoth())
}
predicate setWorldWritable(FunctionCall fc, int mode) {
fc.getTarget().getName() = ["chmod", "fchmod", "_chmod", "_wchmod"] and
mode = fc.getArgument(1).getValue().toInt() and
sets(mode, s_iwoth())
setsAnyBits(mode, UnixConstants::s_iwoth())
}
from Expr fc, int mode, string message

View File

@@ -1,5 +1,49 @@
import cpp
import semmle.code.cpp.commons.unix.Constants
import semmle.code.cpp.commons.unix.Constants as UnixConstants
/**
* Gets the number corresponding to the contents of `input` in base-16.
* Note: the first two characters of `input` must be `0x`. For example:
* `parseHex("0x123abc") = 1194684`.
*/
bindingset[input]
int parseHex(string input) {
exists(string lowerCaseInput | lowerCaseInput = input.toLowerCase() |
lowerCaseInput.regexpMatch("0x[0-9a-f]+") and
result =
strictsum(int ix |
ix in [2 .. input.length()]
|
16.pow(input.length() - (ix + 1)) * "0123456789abcdef".indexOf(lowerCaseInput.charAt(ix))
)
)
}
/**
* Gets the value defined by the `O_CREAT` macro if the macro
* exists and if every definition defines the same value.
*/
int o_creat() {
result =
unique(int v |
exists(Macro m | m.getName() = "O_CREAT" |
v = parseHex(m.getBody()) or v = UnixConstants::parseOctal(m.getBody())
)
)
}
/**
* Gets the value defined by the `O_TMPFILE` macro if the macro
* exists and if every definition defines the same value.
*/
int o_tmpfile() {
result =
unique(int v |
exists(Macro m | m.getName() = "O_TMPFILE" |
v = parseHex(m.getBody()) or v = UnixConstants::parseOctal(m.getBody())
)
)
}
bindingset[n, digit]
private string octalDigit(int n, int digit) {
@@ -20,11 +64,17 @@ string octalFileMode(int mode) {
else result = "[non-standard mode: decimal " + mode + "]"
}
/**
* Holds if the bitmask `value` sets the bits in `flag`.
*/
bindingset[value, flag]
predicate setsFlag(int value, int flag) { value.bitAnd(flag) = flag }
/**
* Holds if the bitmask `mask` sets any of the bit fields in `fields`.
*/
bindingset[mask, fields]
predicate sets(int mask, int fields) { mask.bitAnd(fields) != 0 }
predicate setsAnyBits(int mask, int fields) { mask.bitAnd(fields) != 0 }
/**
* Gets the value that `fc` sets the umask to, if `fc` is a call to
@@ -83,16 +133,24 @@ abstract class FileCreationExpr extends FunctionCall {
abstract int getMode();
}
class OpenCreationExpr extends FileCreationExpr {
abstract class FileCreationWithOptionalModeExpr extends FileCreationExpr {
abstract predicate hasModeArgument();
}
class OpenCreationExpr extends FileCreationWithOptionalModeExpr {
OpenCreationExpr() {
this.getTarget().getName() = ["open", "_open", "_wopen"] and
sets(this.getArgument(1).getValue().toInt(), o_creat())
this.getTarget().hasGlobalOrStdName(["open", "_open", "_wopen"]) and
exists(int flag | flag = this.getArgument(1).getValue().toInt() |
setsFlag(flag, o_creat()) or setsFlag(flag, o_tmpfile())
)
}
override Expr getPath() { result = this.getArgument(0) }
override predicate hasModeArgument() { exists(this.getArgument(2)) }
override int getMode() {
if exists(this.getArgument(2))
if this.hasModeArgument()
then result = this.getArgument(2).getValue().toInt()
else
// assume anything is permitted
@@ -108,20 +166,35 @@ class CreatCreationExpr extends FileCreationExpr {
override int getMode() { result = this.getArgument(1).getValue().toInt() }
}
class OpenatCreationExpr extends FileCreationExpr {
class OpenatCreationExpr extends FileCreationWithOptionalModeExpr {
OpenatCreationExpr() {
this.getTarget().getName() = "openat" and
this.getNumberOfArguments() = 4
this.getTarget().hasGlobalOrStdName("openat") and
exists(int flag | flag = this.getArgument(2).getValue().toInt() |
setsFlag(flag, o_creat()) or setsFlag(flag, o_tmpfile())
)
}
override Expr getPath() { result = this.getArgument(1) }
override int getMode() { result = this.getArgument(3).getValue().toInt() }
override predicate hasModeArgument() { exists(this.getArgument(3)) }
override int getMode() {
if this.hasModeArgument()
then result = this.getArgument(3).getValue().toInt()
else
// assume anything is permitted
result = 0.bitNot()
}
}
private int fopenMode() {
result =
s_irusr().bitOr(s_irgrp()).bitOr(s_iroth()).bitOr(s_iwusr()).bitOr(s_iwgrp()).bitOr(s_iwoth())
UnixConstants::s_irusr()
.bitOr(UnixConstants::s_irgrp())
.bitOr(UnixConstants::s_iroth())
.bitOr(UnixConstants::s_iwusr())
.bitOr(UnixConstants::s_iwgrp())
.bitOr(UnixConstants::s_iwoth())
}
class FopenCreationExpr extends FileCreationExpr {
@@ -153,6 +226,6 @@ class FopensCreationExpr extends FileCreationExpr {
// fopen_s has restrictive permissions unless you have "u" in the mode
if this.getArgument(2).getValue().charAt(_) = "u"
then result = fopenMode()
else result = s_irusr().bitOr(s_iwusr())
else result = UnixConstants::s_irusr().bitOr(UnixConstants::s_iwusr())
}
}

View File

@@ -0,0 +1,9 @@
int open_file_bad() {
// BAD - this uses arbitrary bytes from the stack as mode argument
return open(FILE, O_CREAT)
}
int open_file_good() {
// GOOD - the mode argument is supplied
return open(FILE, O_CREAT, S_IRUSR | S_IWUSR)
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
When opening a file with the <code>O_CREAT</code> or <code>O_TMPFILE</code> flag, the <code>mode</code> must
be supplied. If the <code>mode</code> argument is omitted, some arbitrary bytes from the stack will be used
as the file mode. This leaks some bits from the stack into the permissions of the file.
</p>
</overview>
<recommendation>
<p>
The <code>mode</code> must be supplied when <code>O_CREAT</code> or <code>O_TMPFILE</code> is specified.
</p>
</recommendation>
<example>
<p>
The first example opens a file with the <code>O_CREAT</code> flag without supplying the <code>mode</code>
argument. In this case arbitrary bytes from the stack will be used as <code>mode</code> argument. The
second example correctly supplies the <code>mode</code> argument and creates a file that is user readable
and writable.
</p>
<sample src="OpenCallMissingModeArgument.c" />
</example>
</qhelp>

View File

@@ -0,0 +1,19 @@
/**
* @name File opened with O_CREAT flag but without mode argument
* @description Opening a file with the O_CREAT flag but without mode argument reads arbitrary bytes from the stack.
* @kind problem
* @problem.severity error
* @security-severity 7.8
* @precision high
* @id cpp/open-call-with-mode-argument
* @tags security
* external/cwe/cwe-732
*/
import cpp
import FilePermissions
from FileCreationWithOptionalModeExpr fc
where not fc.hasModeArgument()
select fc,
"A file is created here without providing a mode argument, which may leak bits from the stack."

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query now finds more results, where a password is stored in a struct field or class member variable.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/cleartext-storage-buffer` query has been updated to use the `semmle.code.cpp.dataflow.TaintTracking` library.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/cleartext-storage-file` query has been improved, removing false positives where data is written to a standard output stream.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been further improved to reduce false positive results, and upgraded from `medium` to `high` precision.

View File

@@ -0,0 +1,9 @@
void test(){
int a = 8;
int b = 9;
//Useless NonEquals
if(a==8 && a != 7) {}
while(a==8 && a!=7){}
}

View File

@@ -0,0 +1,18 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Comparison operations like <code>a==8 &amp;&amp; a!=7</code> contain a useless part : the non-equal part. This rule finds tests of this kind within an <code>if</code> or a <code>while</code> statement</p>
</overview>
<recommendation>
<p>Remove the useless comparisons</p>
</recommendation>
<example>
<sample src="UselessTest.cpp" />
</example>
</qhelp>

View File

@@ -0,0 +1,45 @@
/**
* @name Useless Test
* @description A boolean condition that is guaranteed to never be evaluated should be deleted.
* @kind problem
* @problem.severity warning
* @id cpp/uselesstest
* @tags reliability
* readability
*/
import cpp
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
predicate sameExpr(Expr e1, Expr e2) { globalValueNumber(e1).getAnExpr() = e2 }
Element nearestParent(Expr e) {
if
e.getParent().(Expr).getConversion*() instanceof ParenthesisExpr or
e.getParent() instanceof IfStmt or
e.getParent() instanceof WhileStmt
then result = e.getParent()
else result = nearestParent(e.getParent())
}
from LogicalAndExpr b, EQExpr eq, NEExpr ne
where
(
b.getAChild*() = eq and
b.getAChild*() = ne and
eq.getParent() instanceof LogicalAndExpr and
ne.getParent() instanceof LogicalAndExpr
) and
(
eq.getLeftOperand() instanceof VariableAccess and ne.getLeftOperand() instanceof VariableAccess
or
eq.getLeftOperand() instanceof PointerDereferenceExpr and
ne.getLeftOperand() instanceof PointerDereferenceExpr
) and
eq.getRightOperand() instanceof Literal and
ne.getRightOperand() instanceof Literal and
eq.getLeftOperand().getFullyConverted().getUnspecifiedType() =
ne.getLeftOperand().getFullyConverted().getUnspecifiedType() and
nearestParent(eq) = nearestParent(ne) and
sameExpr(eq.getLeftOperand(), ne.getLeftOperand())
select ne, "Useless Test"

View File

@@ -1,6 +1,8 @@
name: codeql/cpp-queries
version: 0.0.9-dev
groups: cpp
groups:
- cpp
- queries
dependencies:
codeql/cpp-all: "*"
codeql/suite-helpers: "*"

View File

@@ -93,7 +93,7 @@
private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
* The base class for tests with inline expectations. The test extends this class to provide the actual
* results of the query, which are then compared with the expected results in comments to produce a
* list of failure messages that point out where the actual results differ from the expected
* results.
@@ -123,6 +123,15 @@ abstract class InlineExpectationsTest extends string {
*/
abstract predicate hasActualResult(Location location, string element, string tag, string value);
/**
* Like `hasActualResult`, but returns results that do not require a matching annotation.
* A failure will still arise if there is an annotation that does not match any results, but not vice versa.
* Override this predicate to specify optional results.
*/
predicate hasOptionalResult(Location location, string element, string tag, string value) {
none()
}
final predicate hasFailureMessage(FailureLocatable element, string message) {
exists(ActualResult actualResult |
actualResult.getTest() = this and
@@ -134,7 +143,8 @@ abstract class InlineExpectationsTest extends string {
)
or
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
message = "Unexpected result: " + actualResult.getExpectationText()
message = "Unexpected result: " + actualResult.getExpectationText() and
not actualResult.isOptional()
)
)
or
@@ -243,9 +253,13 @@ private string expectationPattern() {
private newtype TFailureLocatable =
TActualResult(
InlineExpectationsTest test, Location location, string element, string tag, string value
InlineExpectationsTest test, Location location, string element, string tag, string value,
boolean optional
) {
test.hasActualResult(location, element, tag, value)
test.hasActualResult(location, element, tag, value) and
optional = false
or
test.hasOptionalResult(location, element, tag, value) and optional = true
} or
TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) {
exists(TColumn column, string tags |
@@ -277,8 +291,9 @@ class ActualResult extends FailureLocatable, TActualResult {
string element;
string tag;
string value;
boolean optional;
ActualResult() { this = TActualResult(test, location, element, tag, value) }
ActualResult() { this = TActualResult(test, location, element, tag, value, optional) }
override string toString() { result = element }
@@ -289,6 +304,8 @@ class ActualResult extends FailureLocatable, TActualResult {
override string getTag() { result = tag }
override string getValue() { result = value }
predicate isOptional() { optional = true }
}
abstract private class Expectation extends FailureLocatable {

View File

@@ -11332,6 +11332,64 @@ ir.cpp:
# 1447| Type = [Struct] POD_Derived
# 1447| ValueCategory = prvalue
# 1448| getStmt(4): [ReturnStmt] return ...
# 1450| [CopyAssignmentOperator] Inheritance_Test_B& Inheritance_Test_B::operator=(Inheritance_Test_B const&)
# 1450| <params>:
#-----| getParameter(0): [Parameter] (unnamed parameter 0)
#-----| Type = [LValueReferenceType] const Inheritance_Test_B &
# 1450| [Constructor] void Inheritance_Test_B::Inheritance_Test_B()
# 1450| <params>:
# 1451| [Destructor] void Inheritance_Test_B::~Inheritance_Test_B()
# 1451| <params>:
# 1451| getEntryPoint(): [BlockStmt] { ... }
# 1451| getStmt(0): [ReturnStmt] return ...
# 1451| <destructions>:
# 1454| [CopyAssignmentOperator] Inheritance_Test_A& Inheritance_Test_A::operator=(Inheritance_Test_A const&)
# 1454| <params>:
#-----| getParameter(0): [Parameter] (unnamed parameter 0)
#-----| Type = [LValueReferenceType] const Inheritance_Test_A &
# 1454| [MoveAssignmentOperator] Inheritance_Test_A& Inheritance_Test_A::operator=(Inheritance_Test_A&&)
# 1454| <params>:
#-----| getParameter(0): [Parameter] (unnamed parameter 0)
#-----| Type = [RValueReferenceType] Inheritance_Test_A &&
# 1454| [CopyConstructor] void Inheritance_Test_A::Inheritance_Test_A(Inheritance_Test_A const&)
# 1454| <params>:
#-----| getParameter(0): [Parameter] (unnamed parameter 0)
#-----| Type = [LValueReferenceType] const Inheritance_Test_A &
# 1454| [MoveConstructor] void Inheritance_Test_A::Inheritance_Test_A(Inheritance_Test_A&&)
# 1454| <params>:
#-----| getParameter(0): [Parameter] (unnamed parameter 0)
#-----| Type = [RValueReferenceType] Inheritance_Test_A &&
# 1454| [Destructor] void Inheritance_Test_A::~Inheritance_Test_A()
# 1454| <params>:
# 1457| [Constructor] void Inheritance_Test_A::Inheritance_Test_A()
# 1457| <params>:
# 1457| <initializations>:
# 1457| getInitializer(0): (no string representation)
# 1457| Type = [Struct] Inheritance_Test_B
# 1457| ValueCategory = prvalue
# 1457| getInitializer(1): [ConstructorFieldInit] constructor init of field x
# 1457| Type = [IntType] int
# 1457| ValueCategory = prvalue
# 1457| getExpr(): [Literal] 42
# 1457| Type = [IntType] int
# 1457| Value = [Literal] 42
# 1457| ValueCategory = prvalue
# 1457| getEntryPoint(): [BlockStmt] { ... }
# 1458| getStmt(0): [ExprStmt] ExprStmt
# 1458| getExpr(): [AssignExpr] ... = ...
# 1458| Type = [IntType] int
# 1458| ValueCategory = lvalue
# 1458| getLValue(): [PointerFieldAccess] y
# 1458| Type = [IntType] int
# 1458| ValueCategory = lvalue
# 1458| getQualifier(): [ThisExpr] this
# 1458| Type = [PointerType] Inheritance_Test_A *
# 1458| ValueCategory = prvalue(load)
# 1458| getRValue(): [Literal] 3
# 1458| Type = [IntType] int
# 1458| Value = [Literal] 3
# 1458| ValueCategory = prvalue
# 1459| getStmt(1): [ReturnStmt] return ...
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| <params>:

View File

@@ -1447,4 +1447,15 @@ void temporary_hierarchy() {
float f = (returnValue<POD_Derived>()).f();
}
struct Inheritance_Test_B {
~Inheritance_Test_B() {}
};
struct Inheritance_Test_A : public Inheritance_Test_B {
int x;
int y;
Inheritance_Test_A() : x(42) {
y = 3;
}
};
// semmle-extractor-options: -std=c++17 --clang

View File

@@ -6626,6 +6626,36 @@
| ir.cpp:1447:44:1447:44 | ChiTotal | total:m1447_5 |
| ir.cpp:1447:44:1447:44 | SideEffect | ~m1447_5 |
| ir.cpp:1447:44:1447:44 | StoreValue | r1447_8 |
| ir.cpp:1451:3:1451:21 | Address | &:r1451_5 |
| ir.cpp:1451:3:1451:21 | Address | &:r1451_5 |
| ir.cpp:1451:3:1451:21 | Address | &:r1451_7 |
| ir.cpp:1451:3:1451:21 | Address | &:r1451_7 |
| ir.cpp:1451:3:1451:21 | ChiPartial | partial:m1451_3 |
| ir.cpp:1451:3:1451:21 | ChiTotal | total:m1451_2 |
| ir.cpp:1451:3:1451:21 | Load | m1451_6 |
| ir.cpp:1451:3:1451:21 | SideEffect | m1451_3 |
| ir.cpp:1451:3:1451:21 | SideEffect | m1451_8 |
| ir.cpp:1457:3:1457:20 | Address | &:r1457_5 |
| ir.cpp:1457:3:1457:20 | Address | &:r1457_5 |
| ir.cpp:1457:3:1457:20 | Address | &:r1457_7 |
| ir.cpp:1457:3:1457:20 | Address | &:r1457_7 |
| ir.cpp:1457:3:1457:20 | ChiPartial | partial:m1457_3 |
| ir.cpp:1457:3:1457:20 | ChiTotal | total:m1457_2 |
| ir.cpp:1457:3:1457:20 | Load | m1457_6 |
| ir.cpp:1457:3:1457:20 | SideEffect | m1457_3 |
| ir.cpp:1457:3:1457:20 | SideEffect | m1458_6 |
| ir.cpp:1457:3:1457:20 | Unary | m1457_6 |
| ir.cpp:1457:26:1457:30 | Address | &:r1457_9 |
| ir.cpp:1457:26:1457:30 | ChiPartial | partial:m1457_11 |
| ir.cpp:1457:26:1457:30 | ChiTotal | total:m1457_8 |
| ir.cpp:1457:26:1457:30 | StoreValue | r1457_10 |
| ir.cpp:1458:5:1458:5 | Address | &:r1458_2 |
| ir.cpp:1458:5:1458:5 | Address | &:r1458_4 |
| ir.cpp:1458:5:1458:5 | Load | m1457_6 |
| ir.cpp:1458:5:1458:5 | Unary | r1458_3 |
| ir.cpp:1458:5:1458:9 | ChiPartial | partial:m1458_5 |
| ir.cpp:1458:5:1458:9 | ChiTotal | total:m1457_12 |
| ir.cpp:1458:9:1458:9 | StoreValue | r1458_1 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |

View File

@@ -7858,6 +7858,44 @@ ir.cpp:
# 1443| v1443_5(void) = AliasedUse : ~m?
# 1443| v1443_6(void) = ExitFunction :
# 1451| void Inheritance_Test_B::~Inheritance_Test_B()
# 1451| Block 0
# 1451| v1451_1(void) = EnterFunction :
# 1451| mu1451_2(unknown) = AliasedDefinition :
# 1451| mu1451_3(unknown) = InitializeNonLocal :
# 1451| r1451_4(glval<unknown>) = VariableAddress[#this] :
# 1451| mu1451_5(glval<Inheritance_Test_B>) = InitializeParameter[#this] : &:r1451_4
# 1451| r1451_6(glval<Inheritance_Test_B>) = Load[#this] : &:r1451_4, ~m?
# 1451| mu1451_7(Inheritance_Test_B) = InitializeIndirection[#this] : &:r1451_6
# 1451| v1451_8(void) = NoOp :
# 1451| v1451_9(void) = ReturnIndirection[#this] : &:r1451_6, ~m?
# 1451| v1451_10(void) = ReturnVoid :
# 1451| v1451_11(void) = AliasedUse : ~m?
# 1451| v1451_12(void) = ExitFunction :
# 1457| void Inheritance_Test_A::Inheritance_Test_A()
# 1457| Block 0
# 1457| v1457_1(void) = EnterFunction :
# 1457| mu1457_2(unknown) = AliasedDefinition :
# 1457| mu1457_3(unknown) = InitializeNonLocal :
# 1457| r1457_4(glval<unknown>) = VariableAddress[#this] :
# 1457| mu1457_5(glval<Inheritance_Test_A>) = InitializeParameter[#this] : &:r1457_4
# 1457| r1457_6(glval<Inheritance_Test_A>) = Load[#this] : &:r1457_4, ~m?
# 1457| mu1457_7(Inheritance_Test_A) = InitializeIndirection[#this] : &:r1457_6
# 1457| r1457_8(glval<int>) = FieldAddress[x] : mu1457_5
# 1457| r1457_9(int) = Constant[42] :
# 1457| mu1457_10(int) = Store[?] : &:r1457_8, r1457_9
# 1458| r1458_1(int) = Constant[3] :
# 1458| r1458_2(glval<unknown>) = VariableAddress[#this] :
# 1458| r1458_3(Inheritance_Test_A *) = Load[#this] : &:r1458_2, ~m?
# 1458| r1458_4(glval<int>) = FieldAddress[y] : r1458_3
# 1458| mu1458_5(int) = Store[?] : &:r1458_4, r1458_1
# 1459| v1459_1(void) = NoOp :
# 1457| v1457_11(void) = ReturnIndirection[#this] : &:r1457_6, ~m?
# 1457| v1457_12(void) = ReturnVoid :
# 1457| v1457_13(void) = AliasedUse : ~m?
# 1457| v1457_14(void) = ExitFunction :
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0

View File

@@ -1,18 +1,10 @@
edges
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
subpaths
nodes
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
| test2.cpp:110:3:110:6 | call to gets | semmle.label | call to gets |
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input | semmle.label | input |
| test.cpp:58:25:58:29 | input indirection | semmle.label | input indirection |
| test.cpp:58:25:58:29 | input indirection | semmle.label | input indirection |
subpaths
#select
| test.cpp:58:3:58:9 | call to sprintf | test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input | This write into buffer 'passwd' may contain unencrypted data from $@ | test.cpp:54:17:54:20 | argv | user input (argv) |
| test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | test2.cpp:110:3:110:6 | call to gets | This write into buffer 'password' may contain unencrypted data from $@ | test2.cpp:110:3:110:6 | call to gets | user input (String read by gets) |
| test.cpp:58:3:58:9 | call to sprintf | test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input | This write into buffer 'passwd' may contain unencrypted data from $@ | test.cpp:54:17:54:20 | argv | user input (a command-line argument) |

View File

@@ -1,4 +1,5 @@
edges
| test2.cpp:63:24:63:31 | password | test2.cpp:63:16:63:20 | call to crypt |
| test3.cpp:17:28:17:36 | password1 | test3.cpp:22:15:22:23 | password1 |
| test3.cpp:17:51:17:59 | password2 | test3.cpp:26:15:26:23 | password2 |
| test3.cpp:45:8:45:15 | password | test3.cpp:47:15:47:22 | password |
@@ -89,11 +90,16 @@ edges
| test3.cpp:398:18:398:25 | password | test3.cpp:400:15:400:23 | & ... |
| test3.cpp:398:18:398:25 | password | test3.cpp:400:16:400:23 | password |
| test3.cpp:398:18:398:25 | password | test3.cpp:400:33:400:40 | password |
| test3.cpp:421:21:421:28 | password | test3.cpp:421:3:421:17 | call to decrypt_inplace |
| test3.cpp:429:7:429:14 | password | test3.cpp:431:8:431:15 | password |
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:21:48:27 | call to encrypt |
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:29:48:39 | thePassword |
| test.cpp:66:23:66:43 | cleartext password! | test.cpp:76:21:76:27 | call to encrypt |
| test.cpp:66:23:66:43 | cleartext password! | test.cpp:76:29:76:39 | thePassword |
nodes
| test2.cpp:63:16:63:20 | call to crypt | semmle.label | call to crypt |
| test2.cpp:63:24:63:31 | password | semmle.label | password |
| test2.cpp:63:24:63:31 | password | semmle.label | password |
| test3.cpp:17:28:17:36 | password1 | semmle.label | password1 |
| test3.cpp:17:51:17:59 | password2 | semmle.label | password2 |
| test3.cpp:22:15:22:23 | password1 | semmle.label | password1 |
@@ -208,6 +214,13 @@ nodes
| test3.cpp:400:15:400:23 | & ... | semmle.label | & ... |
| test3.cpp:400:16:400:23 | password | semmle.label | password |
| test3.cpp:400:33:400:40 | password | semmle.label | password |
| test3.cpp:414:17:414:24 | password | semmle.label | password |
| test3.cpp:420:17:420:24 | password | semmle.label | password |
| test3.cpp:421:3:421:17 | call to decrypt_inplace | semmle.label | call to decrypt_inplace |
| test3.cpp:421:21:421:28 | password | semmle.label | password |
| test3.cpp:421:21:421:28 | password | semmle.label | password |
| test3.cpp:429:7:429:14 | password | semmle.label | password |
| test3.cpp:431:8:431:15 | password | semmle.label | password |
| test.cpp:41:23:41:43 | cleartext password! | semmle.label | cleartext password! |
| test.cpp:48:21:48:27 | call to encrypt | semmle.label | call to encrypt |
| test.cpp:48:29:48:39 | thePassword | semmle.label | thePassword |
@@ -238,3 +251,6 @@ subpaths
| test3.cpp:300:2:300:5 | call to send | test3.cpp:308:58:308:66 | password2 | test3.cpp:300:14:300:17 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:308:58:308:66 | password2 | password2 |
| test3.cpp:341:4:341:7 | call to recv | test3.cpp:339:9:339:16 | password | test3.cpp:341:16:341:23 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:339:9:339:16 | password | password |
| test3.cpp:388:3:388:6 | call to recv | test3.cpp:386:8:386:15 | password | test3.cpp:388:15:388:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:386:8:386:15 | password | password |
| test3.cpp:414:3:414:6 | call to recv | test3.cpp:414:17:414:24 | password | test3.cpp:414:17:414:24 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:414:17:414:24 | password | password |
| test3.cpp:420:3:420:6 | call to recv | test3.cpp:420:17:420:24 | password | test3.cpp:420:17:420:24 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:420:17:420:24 | password | password |
| test3.cpp:431:2:431:6 | call to fgets | test3.cpp:429:7:429:14 | password | test3.cpp:431:8:431:15 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:429:7:429:14 | password | password |

View File

@@ -99,3 +99,14 @@ void tests(FILE *log, myStruct &s)
fprintf(log, "log: %s", buffer); // BAD
}
}
char *gets(char *s);
void test_gets()
{
{
char password[1024];
gets(password); // BAD
}
}

View File

@@ -411,13 +411,66 @@ void test_member_password()
{
packet p;
recv(val(), p.password, 256, val()); // BAD: not encrypted [NOT DETECTED]
recv(val(), p.password, 256, val()); // BAD: not encrypted
}
{
packet p;
recv(val(), p.password, 256, val()); // GOOD: password is encrypted
recv(val(), p.password, 256, val()); // GOOD: password is encrypted [FALSE POSITIVE]
decrypt_inplace(p.password); // proof that `password` was in fact encrypted
}
}
extern FILE *stdin;
void test_stdin_param(FILE *stream)
{
char password[128];
fgets(password, 128, stream); // GOOD: from standard input (see call below) [FALSE POSITIVE]
}
void test_stdin()
{
char password[128];
FILE *f = stdin;
fgets(password, 128, stdin); // GOOD: from standard input
fgets(password, 128, f); // GOOD: from standard input
test_stdin_param(stdin);
}
int open(const char *filename, int b);
void test_tty()
{
{
char password[256];
int f;
f = open("/dev/tty", val());
recv(f, password, 256, val()); // GOOD: from terminal
}
{
char password[256];
int f;
f = STDIN_FILENO;
recv(f, password, 256, val()); // GOOD: from stdin
}
{
char password[256];
int f;
f = open("/dev/tty", val());
if (f == -1)
{
f = STDIN_FILENO;
}
recv(f, password, 256, val()); // GOOD: from terminal or stdin
}
}

View File

@@ -0,0 +1,29 @@
typedef unsigned int mode_t;
#define O_RDWR 0x0002
#define O_CLOEXEC 0x0040
#define O_NONBLOCK 0x0080
#define O_CREAT 0x0200
#define O_APPEND 0x0800
#define O_TMPFILE 0x2000
int open(const char *pathname, int flags, ...);
int openat(int dirfd, const char *pathname, int flags, ...);
const char *a_file = "/a_file";
void test_open() {
open(a_file, O_NONBLOCK); // GOOD
open(a_file, O_RDWR | O_CLOEXEC); // GOOD
open(a_file, O_APPEND); // GOOD
open(a_file, O_CREAT); // BAD
open(a_file, O_CREAT, 0); // GOOD
open(a_file, O_TMPFILE); // BAD
open(a_file, O_TMPFILE, 0); // GOOD
openat(0, a_file, O_APPEND); // GOOD
openat(0, a_file, O_CREAT); // BAD
openat(0, a_file, O_CREAT, 0); // GOOD
openat(0, a_file, O_TMPFILE); // BAD
openat(0, a_file, O_TMPFILE, 0); // GOOD
}

View File

@@ -0,0 +1,4 @@
| OpenCallMissingModeArgument.c:20:3:20:6 | call to open | A file is created here without providing a mode argument, which may leak bits from the stack. |
| OpenCallMissingModeArgument.c:22:3:22:6 | call to open | A file is created here without providing a mode argument, which may leak bits from the stack. |
| OpenCallMissingModeArgument.c:25:3:25:8 | call to openat | A file is created here without providing a mode argument, which may leak bits from the stack. |
| OpenCallMissingModeArgument.c:27:3:27:8 | call to openat | A file is created here without providing a mode argument, which may leak bits from the stack. |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-732/OpenCallMissingModeArgument.ql