Skip to content

Commit

Permalink
Adds MySQL/codeql/ and MySQL/clang_checker
Browse files Browse the repository at this point in the history
  • Loading branch information
ZDI committed Feb 23, 2022
1 parent ec6ae36 commit 33100cb
Show file tree
Hide file tree
Showing 4 changed files with 396 additions and 0 deletions.
197 changes: 197 additions & 0 deletions MySQL/clang_checker/TaintedLoopChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/AST/ParentMap.h"

using namespace clang;
using namespace ento;
using namespace taint;

namespace {

class TaintedLoopChecker : public Checker<check::BranchCondition> {

mutable std::unique_ptr<BugType> BT;

public:
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
bool IsLoop(const Stmt *Stmt, CheckerContext &Ctx) const;
void CheckLoopCondition(const Stmt *stmt, CheckerContext &Ctx) const;
bool CheckTaintedStmt(const Stmt *stmt, CheckerContext &Ctx) const;
bool isArgUnConstrained(SVal Arg, SValBuilder &builder, ProgramStateRef state) const;
void reportBug(CheckerContext &C) const;
};
}

void TaintedLoopChecker::reportBug(CheckerContext &Ctx) const {

if (!BT)
BT.reset(new BuiltinBug(this, "Tainted Loop Condition"));

ExplodedNode *N = Ctx.generateNonFatalErrorNode(Ctx.getState());

if (!N)
return;

auto report = std::make_unique<PathSensitiveBugReport>(*BT, "Tainted Branch Condition in Loop Construct", N);

Ctx.emitReport(std::move(report));
}

// Following code is from isArgUnConstrained by Andrew Ruef written for analyzing OpenSSL bug
// https://blog.trailofbits.com/2014/04/27/using-static-analysis-and-clang-to-find-heartbleed

bool TaintedLoopChecker::isArgUnConstrained(SVal Arg, SValBuilder &builder, ProgramStateRef state) const {

bool result = false;

llvm::APInt V(32, 0x10000);
SVal Val = builder.makeIntVal(V, false);

Optional<NonLoc> NLVal = Val.getAs<NonLoc>();

if (!NLVal) {
return result;
}

Optional<NonLoc> NLArg = Arg.getAs<NonLoc>();

if (!NLArg) {
return result;
}

SVal cmprLT = builder.evalBinOp(state,
BO_GT,
*NLArg,
*NLVal,
builder.getConditionType());

Optional<DefinedOrUnknownSVal> NLcmprLT = cmprLT.getAs<DefinedOrUnknownSVal>();

if (!NLcmprLT) {
return result;
}

std::pair<ProgramStateRef,ProgramStateRef> p =
state->assume(*NLcmprLT);

ProgramStateRef trueState = p.first;

if (trueState) {
result = true;
}

return result;
}


bool TaintedLoopChecker::IsLoop(const Stmt *stmt, CheckerContext &Ctx) const {

const ParentMap &PM = Ctx.getLocationContext()->getParentMap();
const Stmt *current_stmt = stmt;

while (PM.hasParent(current_stmt)) {

unsigned int StmtClass = current_stmt->getStmtClass();
current_stmt = PM.getParent(current_stmt);

if (StmtClass == Stmt::CompoundStmtClass)
return false;
else if (StmtClass == Stmt::WhileStmtClass)
return true;
else if (StmtClass == Stmt::DoStmtClass)
return true;
else if (StmtClass == Stmt::ForStmtClass)
return true;
}

return false;
}

bool TaintedLoopChecker::CheckTaintedStmt(const Stmt *stmt, CheckerContext &Ctx) const {

ProgramStateRef state = Ctx.getState();

if (isTainted(state, Ctx.getSVal(stmt)))
return true;

Stmt::const_child_iterator child = stmt->child_begin();

while (child != stmt->child_end()) {

if (isTainted(state, Ctx.getSVal(*child))) {
return true;
}

++child;
}

return false;
}

void TaintedLoopChecker::CheckLoopCondition(const Stmt *Condition, CheckerContext &Ctx) const {

ProgramStateRef state = Ctx.getState();
SValBuilder &svalBuilder = Ctx.getSValBuilder();
SVal LoopVarVal;

if (const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Condition)) {

if (BinOp->isComparisonOp()) {

const Expr *RHS = BinOp->getRHS();
const Expr *LHS = BinOp->getLHS();

SVal RHSVal = Ctx.getSVal(RHS);

if (RHSVal.isZeroConstant())
Condition = LHS;
else
Condition = RHS;

if (TaintedLoopChecker::CheckTaintedStmt(Condition, Ctx)) {

LoopVarVal = Ctx.getSVal(Condition);

if (TaintedLoopChecker::isArgUnConstrained(LoopVarVal, svalBuilder, state))
reportBug(Ctx);
}
}

} else if (TaintedLoopChecker::CheckTaintedStmt(Condition, Ctx)) {

// handle possible implicit boolean conversions
if (dyn_cast<Expr>(Condition)->isKnownToHaveBooleanValue()) {

if (const ImplicitCastExpr *IE = dyn_cast<ImplicitCastExpr>(Condition)) {
Condition = IE->getSubExpr();
}
}

if (const UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Condition)) {
Condition = UnOp->getSubExpr();
}

LoopVarVal = Ctx.getSVal(Condition);

if (TaintedLoopChecker::isArgUnConstrained(LoopVarVal, svalBuilder, state))
reportBug(Ctx);
}
}

void TaintedLoopChecker::checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {

if (IsLoop(Condition, Ctx))
CheckLoopCondition(Condition, Ctx);
}

void ento::registerTaintedLoopChecker(CheckerManager &mgr) {
mgr.registerChecker<TaintedLoopChecker>();
}

bool ento::shouldRegisterTaintedLoopChecker(const CheckerManager &mgr) {
return true;
}
58 changes: 58 additions & 0 deletions MySQL/clang_checker/TaintedPointerChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/AST/ParentMap.h"

using namespace clang;
using namespace ento;
using namespace taint;

namespace {

class TaintedPointerChecker : public Checker<check::Location> {

mutable std::unique_ptr<BugType> BT;

public:
void checkLocation(SVal L, bool IsLoad, const Stmt *S, CheckerContext &Ctx) const;
void reportBug(CheckerContext &C) const;
};
}

void TaintedPointerChecker::reportBug(CheckerContext &Ctx) const {

if (!BT)
BT.reset(new BuiltinBug(this, "Tainted Pointer Load"));

ExplodedNode *N = Ctx.generateNonFatalErrorNode(Ctx.getState());

if (!N)
return;

auto report = std::make_unique<PathSensitiveBugReport>(*BT, "Pointer Loaded From Tainted Source", N);

Ctx.emitReport(std::move(report));
}

void TaintedPointerChecker::checkLocation(SVal L, bool IsLoad, const Stmt *stmt, CheckerContext &Ctx) const {

if (!IsLoad)
return;

if (isTainted(Ctx.getState(), L)) {
const Expr *expr = dyn_cast<Expr>(stmt)->IgnoreImplicitAsWritten();
if (expr && expr->getType()->isPointerType())
reportBug(Ctx);
}
}

void ento::registerTaintedPointerChecker(CheckerManager &mgr) {
mgr.registerChecker<TaintedPointerChecker>();
}

bool ento::shouldRegisterTaintedPointerChecker(const CheckerManager &mgr) {
return true;
}
72 changes: 72 additions & 0 deletions MySQL/codeql/iruntrusted.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @id cpp/untrusted-pointer-dereference
* @kind path-problem
* @problem.severity error
*/

import cpp
import DataFlow::PathGraph
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasedSSA

//ref https://msrc-blog.microsoft.com/2019/03/19/vulnerability-hunting-with-semmle-ql-part-2/

class SystemCfg extends TaintTracking::Configuration {
SystemCfg() { this = "SystemCfg" }

override predicate isSource(DataFlow::Node node) {
exists (FieldAccess va |
node.asExpr() = va
and va.getTarget().hasName("theData")
)
}

override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Expr e, FieldAccess fa |
pred.asExpr() = e
and fa.getQualifier*() = e
and succ.asExpr() = fa
)
}

override predicate isSink(DataFlow::Node node) {
exists(Instruction ir, string var, VariableAccess va |
ir instanceof LoadInstruction
and ir.getResultIRType() instanceof IRAddressType
and ir.(LoadInstruction).getSourceValueOperand().isDefinitionInexact()
and node.asExpr() = ir.getAST().(Expr)
// Check type info of virtual variable to filter results. Very specific to MySQL Cluster, rewrite as necessary
and va.getEnclosingFunction().getName() = ir.getEnclosingFunction().getName()
and var = getOperandMemoryLocation(ir.(LoadInstruction).getSourceValueOperand()).getVirtualVariable().toString().replaceAll("*", "")
and va.getTarget().toString() = var
and va.getTarget().getType().toString().matches("%Signal%")
)
}

override predicate isSanitizer(DataFlow::Node node) {
exists(MacroInvocation mi |
mi.getMacroName().matches("%ptrCheckGuard%")
and mi.getExpr() = node.asExpr()
) or

exists(MacroInvocation mi |
mi.getMacroName().matches("%arrGuard%")
and mi.getExpr() = node.asExpr()
) or

exists( IfStmt aif, RelationalOperation rop |
node.asExpr().(VariableAccess).getTarget().getAnAccess() = aif.getControllingExpr().getAChild*()
and aif.getASuccessor+() = node.asExpr()
and not ( node.asExpr() = aif.getControllingExpr().getAChild*() )
and rop = aif.getControllingExpr().getAChild*()
and not rop.getRightOperand().getValue().toInt() = 0 // ignore check against 0
)
}
}

from DataFlow::PathNode sink, DataFlow::PathNode source, SystemCfg cfg
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, sink.getNode().getLocation().toString()
69 changes: 69 additions & 0 deletions MySQL/codeql/loop.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @id cpp/untrusted-loop
* @kind path-problem
* @problem.severity warning
*/

import cpp
import DataFlow::PathGraph
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.valuenumbering.GlobalValueNumbering

//ref https://msrc-blog.microsoft.com/2019/03/19/vulnerability-hunting-with-semmle-ql-part-2/

class SystemCfg extends TaintTracking::Configuration {
SystemCfg() { this = "SystemCfg" }

override predicate isSource(DataFlow::Node node) {
exists (FieldAccess va |
node.asExpr() = va
and va.getTarget().hasName("theData")
)
}

override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Expr e, FieldAccess fa |
pred.asExpr() = e
and fa.getQualifier*() = e
and succ.asExpr() = fa
)
}

override predicate isSink(DataFlow::Node node) {
exists(Loop lp, Expr cexpr |
cexpr = lp.getControllingExpr() and (
(
cexpr.(ComparisonOperation).getRightOperand().getValue().toInt() = 0
and node.asExpr() = cexpr.(ComparisonOperation).getLeftOperand()
)
or node.asExpr() = cexpr.(ComparisonOperation).getRightOperand()
or node.asExpr() = cexpr.(UnaryOperation).getOperand()
)
)
}

override predicate isSanitizer(DataFlow::Node node) {
exists(MacroInvocation mi |
mi.getMacroName().matches("%ptrCheckGuard%")
and mi.getExpr() = node.asExpr()
) or

exists(MacroInvocation mi |
mi.getMacroName().matches("%arrGuard%")
and mi.getExpr() = node.asExpr()
) or

exists( IfStmt aif, RelationalOperation rop |
node.asExpr().(VariableAccess).getTarget().getAnAccess() = aif.getControllingExpr().getAChild*()
and aif.getASuccessor+() = node.asExpr()
and not ( node.asExpr() = aif.getControllingExpr().getAChild*() )
and rop = aif.getControllingExpr().getAChild*()
and not rop.getRightOperand().getValue().toInt() = 0 // ignore check against 0
)
}
}

from DataFlow::PathNode sink, DataFlow::PathNode source, SystemCfg cfg
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, sink.getNode().getLocation().toString()

0 comments on commit 33100cb

Please sign in to comment.