mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge remote-tracking branch 'upstream/master' into dbartol/VarArgIR
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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() + ":" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() + ":" }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() + ":" }
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal file
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user