Merge remote-tracking branch 'upstream/master' into dbartol/VarArgIR

This commit is contained in:
Dave Bartolomeo
2020-03-18 09:52:21 -04:00
67 changed files with 1984 additions and 466 deletions

View File

@@ -19,15 +19,19 @@ class ConstantZero extends Expr {
* Holds if `candidate` is an expression such that if it's unsigned then we
* want an alert at `ge`.
*/
private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
// Base case: `candidate >= 0`
ge.getRightOperand() instanceof ConstantZero and
candidate = ge.getLeftOperand().getFullyConverted() and
// left operand was a signed or unsigned IntegralType before conversions
private predicate lookForUnsignedAt(RelationalOperation ge, Expr candidate) {
// Base case: `candidate >= 0` (or `0 <= candidate`)
(
ge instanceof GEExpr or
ge instanceof LEExpr
) and
ge.getLesserOperand() instanceof ConstantZero and
candidate = ge.getGreaterOperand().getFullyConverted() and
// left/greater operand was a signed or unsigned IntegralType before conversions
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
// (not an enum, as the fully converted type of an enum is compiler dependent
// so checking an enum >= 0 is always reasonable)
ge.getLeftOperand().getUnderlyingType() instanceof IntegralType
ge.getGreaterOperand().getUnderlyingType() instanceof IntegralType
or
// Recursive case: `...(largerType)candidate >= 0`
exists(Conversion conversion |
@@ -37,7 +41,7 @@ private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
)
}
class UnsignedGEZero extends GEExpr {
class UnsignedGEZero extends ComparisonOperation {
UnsignedGEZero() {
exists(Expr ue |
lookForUnsignedAt(this, ue) and

View File

@@ -0,0 +1,138 @@
#include <stdlib.h>
#include <sys/param.h>
#include <unistd.h>
#include <pwd.h>
void callSetuidAndCheck(int uid) {
if (setuid(uid) != 0) {
exit(1);
}
}
void callSetgidAndCheck(int gid) {
if (setgid(gid) != 0) {
exit(1);
}
}
/// Correct ways to drop priv.
void correctDropPrivInline() {
if (setgroups(0, NULL)) {
exit(1);
}
if (setgid(-2) != 0) {
exit(1);
}
if (setuid(-2) != 0) {
exit(1);
}
}
void correctDropPrivInScope() {
{
if (setgroups(0, NULL)) {
exit(1);
}
}
{
if (setgid(-2) != 0) {
exit(1);
}
}
{
if (setuid(-2) != 0) {
exit(1);
}
}
}
void correctOrderForInitgroups() {
struct passwd *pw = getpwuid(0);
if (pw) {
if (initgroups(pw->pw_name, -2)) {
exit(1);
}
} else {
// Unhandled.
}
int rc = setuid(-2);
if (rc) {
exit(1);
}
}
void correctDropPrivInScopeParent() {
{
callSetgidAndCheck(-2);
}
correctOrderForInitgroups();
}
void incorrectNoReturnCodeCheck() {
int user = -2;
if (user) {
if (user) {
int rc = setgid(user);
(void)rc;
initgroups("nobody", user);
}
if (user) {
setuid(user);
}
}
}
void correctDropPrivInFunctionCall() {
if (setgroups(0, NULL)) {
exit(1);
}
callSetgidAndCheck(-2);
callSetuidAndCheck(-2);
}
/// Incorrect, out of order gid and uid.
/// Calling uid before gid will fail.
void incorrectDropPrivOutOfOrderInline() {
if (setuid(-2) != 0) {
exit(1);
}
if (setgid(-2) != 0) {
exit(1);
}
}
void incorrectDropPrivOutOfOrderInScope() {
{
if (setuid(-2) != 0) {
exit(1);
}
}
setgid(-2);
}
void incorrectDropPrivOutOfOrderWithFunction() {
callSetuidAndCheck(-2);
if (setgid(-2) != 0) {
exit(1);
}
}
void incorrectDropPrivOutOfOrderWithFunction2() {
callSetuidAndCheck(-2);
callSetgidAndCheck(-2);
}
void incorrectDropPrivNoCheck() {
setgid(-2);
setuid(-2);
}

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The code attempts to drop privilege in an incorrect order by
erroneous dropping user privilege before groups. This has security
impact if the return codes are not checked.</p>
<p>False positives include code performing negative checks, making
sure that setgid or setgroups does not work, meaning permissions are
dropped. Additionally, other forms of sandboxing may be present removing
any residual risk, for example a dedicated user namespace.</p>
</overview>
<recommendation>
<p>Set the new group ID, then set the target user's intended groups by
dropping previous supplemental source groups and initializing target
groups, and finally set the target user.</p>
</recommendation>
<example>
<p>The following example demonstrates out of order calls.</p>
<sample src="PrivilegeDroppingOutoforder.c" />
</example>
<references>
<li>CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS37-C.+Ensure+that+privilege+relinquishment+is+successful">POS37-C. Ensure that privilege relinquishment is successful</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,101 @@
/**
* @name LinuxPrivilegeDroppingOutoforder
* @description A syscall commonly associated with privilege dropping is being called out of order.
* Normally a process drops group ID and sets supplimental groups for the target user
* before setting the target user ID. This can have security impact if the return code
* from these methods is not checked.
* @kind problem
* @problem.severity recommendation
* @id cpp/drop-linux-privileges-outoforder
* @tags security
* external/cwe/cwe-273
* @precision medium
*/
import cpp
predicate argumentMayBeRoot(Expr e) {
e.getValue() = "0" or
e.(VariableAccess).getTarget().getName().toLowerCase().matches("%root%")
}
class SetuidLikeFunctionCall extends FunctionCall {
SetuidLikeFunctionCall() {
(getTarget().hasGlobalName("setuid") or getTarget().hasGlobalName("setresuid")) and
// setuid/setresuid with the root user are false positives.
not argumentMayBeRoot(getArgument(0))
}
}
class SetuidLikeWrapperCall extends FunctionCall {
SetuidLikeFunctionCall baseCall;
SetuidLikeWrapperCall() {
this = baseCall
or
exists(SetuidLikeWrapperCall fc |
this.getTarget() = fc.getEnclosingFunction() and
baseCall = fc.getBaseCall()
)
}
SetuidLikeFunctionCall getBaseCall() { result = baseCall }
}
class CallBeforeSetuidFunctionCall extends FunctionCall {
CallBeforeSetuidFunctionCall() {
(
getTarget().hasGlobalName("setgid") or
getTarget().hasGlobalName("setresgid") or
// Compatibility may require skipping initgroups and setgroups return checks.
// A stricter best practice is to check the result and errnor for EPERM.
getTarget().hasGlobalName("initgroups") or
getTarget().hasGlobalName("setgroups")
) and
// setgid/setresgid/etc with the root group are false positives.
not argumentMayBeRoot(getArgument(0))
}
}
class CallBeforeSetuidWrapperCall extends FunctionCall {
CallBeforeSetuidFunctionCall baseCall;
CallBeforeSetuidWrapperCall() {
this = baseCall
or
exists(CallBeforeSetuidWrapperCall fc |
this.getTarget() = fc.getEnclosingFunction() and
baseCall = fc.getBaseCall()
)
}
CallBeforeSetuidFunctionCall getBaseCall() { result = baseCall }
}
predicate setuidBeforeSetgid(
SetuidLikeWrapperCall setuidWrapper, CallBeforeSetuidWrapperCall setgidWrapper
) {
setgidWrapper.getAPredecessor+() = setuidWrapper
}
predicate isAccessed(FunctionCall fc) {
exists(Variable v | v.getAnAssignedValue() = fc)
or
exists(Operation c | fc = c.getAChild() | c.isCondition())
or
// ignore pattern where result is intentionally ignored by a cast to void.
fc.hasExplicitConversion()
}
from Function func, CallBeforeSetuidFunctionCall fc, SetuidLikeFunctionCall setuid
where
setuidBeforeSetgid(setuid, fc) and
// Require the call return code to be used in a condition or assigned.
// This introduces false negatives where the return is checked but then
// errno == EPERM allows execution to continue.
not isAccessed(fc) and
func = fc.getEnclosingFunction()
select fc,
"This function is called within " + func + ", and potentially after " +
"$@, and may not succeed. Be sure to check the return code and errno, otherwise permissions " +
"may not be dropped.", setuid, setuid.getTarget().getName()

View File

@@ -381,10 +381,10 @@ class StaticStorageDurationVariable extends Variable {
}
/**
* Holds if the initializer for this variable is evaluated at compile time.
* Holds if the initializer for this variable is evaluated at runtime.
*/
predicate hasConstantInitialization() {
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
predicate hasDynamicInitialization() {
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
}
}
@@ -409,6 +409,11 @@ private predicate inStaticInitializer(Expr e) {
inStaticInitializer(e.getParent())
}
/**
* A C++ local variable declared as `static`.
*/
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
/**
* A C/C++ variable which has global scope or namespace scope. For example the
* variables `a` and `b` in the following code:

View File

@@ -441,9 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
* thus should not have control flow computed.
*/
private predicate skipInitializer(Initializer init) {
exists(LocalVariable local |
exists(StaticLocalVariable local |
init = local.getInitializer() and
local.(StaticStorageDurationVariable).hasConstantInitialization()
not local.hasDynamicInitialization()
)
}

View File

@@ -76,6 +76,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
@@ -96,6 +98,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
@@ -119,6 +123,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
@@ -153,6 +159,11 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
)
}
private predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
node = getNodeForSource(any(Expr e))
}
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -10,6 +10,11 @@ newtype TIRVariable =
) {
Construction::hasTempVariable(func, ast, tag, type)
} or
TIRDynamicInitializationFlag(
Language::Function func, Language::Variable var, Language::LanguageType type
) {
Construction::hasDynamicInitializationFlag(func, var, type)
} or
TIRStringLiteral(
Language::Function func, Language::AST ast, Language::LanguageType type,
Language::StringLiteral literal

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -51,6 +51,13 @@ private module Cached {
getTypeForPRValue(literal.getType()) = type
}
cached
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
var.getFunction() = func and
var.hasDynamicInitialization() and
type = getBoolType()
}
cached
predicate hasModeledMemoryResult(Instruction instruction) { none() }

View File

@@ -8,6 +8,11 @@ newtype TInstructionTag =
InitializerStoreTag() or
InitializerIndirectAddressTag() or
InitializerIndirectStoreTag() or
DynamicInitializationFlagAddressTag() or
DynamicInitializationFlagLoadTag() or
DynamicInitializationConditionalBranchTag() or
DynamicInitializationFlagConstantTag() or
DynamicInitializationFlagStoreTag() or
ZeroPadStringConstantTag() or
ZeroPadStringElementIndexTag() or
ZeroPadStringElementAddressTag() or
@@ -186,4 +191,14 @@ string getInstructionTagId(TInstructionTag tag) {
tag = AsmTag() and result = "Asm"
or
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
or
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
or
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
or
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
or
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
or
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
}

View File

@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
private import TranslatedInitialization
/**
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
}
/**
* Represents the IR translation of a local variable declaration within a declaration statement.
* The IR translation of a local variable declaration within a declaration statement.
*/
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
TranslatedDeclarationEntry {
LocalVariable var;
StackVariable var;
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
override LocalVariable getVariable() { result = var }
}
/**
* The IR translation of the declaration of a static local variable.
* This element generates the logic that determines whether or not the variable has already been
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
* variable. The actual initialization code is handled in
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
*
* The generated code to do the initialization only once is:
* ```
* Block 1
* r1225_1(glval<bool>) = VariableAddress[c#init] :
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
* v1225_3(void) = ConditionalBranch : r1225_2
* False -> Block 2
* True -> Block 3
*
* Block 2
* r1225_4(glval<int>) = VariableAddress[c] :
* <actual initialization of `c`>
* r1225_8(bool) = Constant[1] :
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
* Goto -> Block 3
*
* Block 3
* ```
*
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
* like any other variable with static storage duration.
*/
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
StaticLocalVariable var;
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
tag = DynamicInitializationFlagAddressTag() and
opcode instanceof Opcode::VariableAddress and
type = getBoolGLValueType()
or
tag = DynamicInitializationFlagLoadTag() and
opcode instanceof Opcode::Load and
type = getBoolType()
or
tag = DynamicInitializationConditionalBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
type = getVoidType()
or
tag = DynamicInitializationFlagConstantTag() and
opcode instanceof Opcode::Constant and
type = getBoolType()
or
tag = DynamicInitializationFlagStoreTag() and
opcode instanceof Opcode::Store and
type = getBoolType()
}
final override Instruction getFirstInstruction() {
result = getInstruction(DynamicInitializationFlagAddressTag())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = DynamicInitializationFlagAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagLoadTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationConditionalBranchTag())
or
tag = DynamicInitializationConditionalBranchTag() and
(
kind instanceof TrueEdge and
result = getParent().getChildSuccessor(this)
or
kind instanceof FalseEdge and
result = getInitialization().getFirstInstruction()
)
or
tag = DynamicInitializationFlagConstantTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagStoreTag())
or
tag = DynamicInitializationFlagStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getInstruction(DynamicInitializationFlagConstantTag())
}
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
tag = DynamicInitializationFlagAddressTag() and
result.getVariable() = var
}
final override string getInstructionConstantValue(InstructionTag tag) {
tag = DynamicInitializationFlagConstantTag() and result = "1"
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = DynamicInitializationFlagLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof LoadOperandTag and
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
)
or
tag = DynamicInitializationConditionalBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(DynamicInitializationFlagConstantTag())
)
}
private TranslatedStaticLocalVariableInitialization getInitialization() {
result.getVariable() = var
}
}
/**
* The initialization of a static local variable. This element will only exist for a static variable
* with a dynamic initializer.
*/
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
VariableDeclarationEntry entry;
StaticLocalVariable var;
TranslatedStaticLocalVariableInitialization() {
this = TTranslatedStaticLocalVariableInitialization(entry) and
var = entry.getDeclaration()
}
final override string toString() { result = "init: " + entry.toString() }
final override Locatable getAST() { result = entry }
final override LocalVariable getVariable() { result = var }
final override Function getFunction() { result = var.getFunction() }
}
/**
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
* `var`.

View File

@@ -54,7 +54,7 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// Otherwise the initializer does not run in function scope.
exists(Initializer init, StaticStorageDurationVariable var |
init = var.getInitializer() and
var.hasConstantInitialization() and
not var.hasDynamicInitialization() and
expr = init.getExpr().getFullyConverted()
)
or
@@ -256,6 +256,26 @@ predicate hasTranslatedLoad(Expr expr) {
not ignoreLoad(expr)
}
/**
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
* necessary for automatic local variables, or for static local variables with dynamic
* initialization.
*/
private predicate translateDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt, LocalVariable var |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
var = entry.getDeclaration() and
(
not var.isStatic()
or
// Ignore static variables unless they have a dynamic initializer.
var.(StaticLocalVariable).hasDynamicInitialization()
)
)
}
newtype TTranslatedElement =
// An expression that is not being consumed as a condition
TTranslatedValueExpr(Expr expr) {
@@ -396,13 +416,12 @@ newtype TTranslatedElement =
)
} or
// A local declaration
TTranslatedDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
entry.getDeclaration() instanceof LocalVariable
)
TTranslatedDeclarationEntry(DeclarationEntry entry) { translateDeclarationEntry(entry) } or
// The dynamic initialization of a static local variable. This is a separate object from the
// declaration entry.
TTranslatedStaticLocalVariableInitialization(DeclarationEntry entry) {
translateDeclarationEntry(entry) and
entry.getDeclaration() instanceof StaticLocalVariable
} or
// A compiler-generated variable to implement a range-based for loop. These don't have a
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.

View File

@@ -115,10 +115,14 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
* evaluating the initializer.
*/
final predicate hasUninitializedInstruction() {
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
(
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
) and
// Variables with static or thread-local storage duration are zero-initialized at program startup.
getIRVariable() instanceof IRAutomaticVariable
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -363,6 +363,11 @@ CppPRValueType getIntType() {
*/
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
/**
* Gets the `CppType` that represents a glvalue of type `bool`.
*/
CppType getBoolGLValueType() { exists(BoolType type | result.hasType(type, true)) }
/**
* Gets the `CppType` that represents a glvalue of function type.
*/

View File

@@ -1,6 +1,7 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fread
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Memcpy

View File

@@ -0,0 +1,45 @@
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard functions `gets` and `fgets`.
*/
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction {
GetsFunction() {
exists(string name | hasGlobalOrStdName(name) |
name = "gets" or // gets(str)
name = "fgets" or // fgets(str, num, stream)
name = "fgetws" // fgetws(wstr, num, stream)
)
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(2) and
output.isParameterDeref(0)
}
override predicate parameterNeverEscapes(int index) { index = 2 }
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
}
}