SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, BinaryOperator::Opcode Op, NonLoc LHS, NonLoc RHS, QualType ResultTy) { if (!State->isTainted(RHS) && !State->isTainted(LHS)) return UnknownVal(); const SymExpr *symLHS = LHS.getAsSymExpr(); const SymExpr *symRHS = RHS.getAsSymExpr(); // TODO: When the Max Complexity is reached, we should conjure a symbol // instead of generating an Unknown value and propagate the taint info to it. const unsigned MaxComp = 10000; // 100000 28X if (symLHS && symRHS && (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) return makeNonLoc(symLHS, Op, symRHS, ResultTy); if (symLHS && symLHS->computeComplexity() < MaxComp) if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); if (symRHS && symRHS->computeComplexity() < MaxComp) if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); return UnknownVal(); }
SVal SValBuilder::makeGenericVal(ProgramStateRef State, BinaryOperator::Opcode Op, NonLoc LHS, NonLoc RHS, QualType ResultTy) { // If operands are tainted, create a symbol to ensure that we propagate taint. if (State->isTainted(RHS) || State->isTainted(LHS)) { const SymExpr *symLHS; const SymExpr *symRHS; if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) { symLHS = LHS.getAsSymExpr(); return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); } if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) { symRHS = RHS.getAsSymExpr(); return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); } symLHS = LHS.getAsSymExpr(); symRHS = RHS.getAsSymExpr(); return makeNonLoc(symLHS, Op, symRHS, ResultTy); } return UnknownVal(); }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const { assert(E); // Check for taint. ProgramStateRef State = C.getState(); Optional<SVal> PointedToSVal = getPointedToSVal(C, E); SVal TaintedSVal; if (PointedToSVal && State->isTainted(*PointedToSVal)) TaintedSVal = *PointedToSVal; else if (State->isTainted(E, C.getLocationContext())) TaintedSVal = C.getSVal(E); else return false; // Generate diagnostic. if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal)); C.emitReport(std::move(report)); return true; } return false; }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const { assert(E); // Check for taint. ProgramStateRef State = C.getState(); if (!State->isTainted(getPointedToSymbol(C, E)) && !State->isTainted(E, C.getLocationContext())) return false; // Generate diagnostic. if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); C.emitReport(std::move(report)); return true; } return false; }
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const { assert(E); // Check for taint. ProgramStateRef State = C.getState(); if (!State->isTainted(getPointedToSymbol(C, E)) && !State->isTainted(E, C.getLocationContext())) return false; // Generate diagnostic. if (ExplodedNode *N = C.addTransition()) { initBugType(); BugReport *report = new BugReport(*BT, Msg, N); report->addRange(E->getSourceRange()); C.emitReport(report); return true; } return false; }
void TaintTesterChecker::checkPostStmt(const Expr *E, CheckerContext &C) const { ProgramStateRef State = C.getState(); if (!State) return; if (State->isTainted(E, C.getLocationContext())) { if (ExplodedNode *N = C.addTransition()) { initBugType(); BugReport *report = new BugReport(*BT, "tainted",N); report->addRange(E->getSourceRange()); C.emitReport(report); } } }
void TaintTesterChecker::checkPostStmt(const Expr *E, CheckerContext &C) const { ProgramStateRef State = C.getState(); if (!State) return; if (State->isTainted(E, C.getLocationContext())) { if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique<BugReport>(*BT, "tainted",N); report->addRange(E->getSourceRange()); C.emitReport(std::move(report)); } } }
// If argument 0 (file descriptor) is tainted, all arguments except for arg 0 // and arg 1 should get taint. ProgramStateRef GenericTaintChecker::preFscanf(const CallExpr *CE, CheckerContext &C) const { assert(CE->getNumArgs() >= 2); ProgramStateRef State = C.getState(); // Check is the file descriptor is tainted. if (State->isTainted(CE->getArg(0), C.getLocationContext()) || isStdin(CE->getArg(0), C)) { // All arguments except for the first two should get taint. for (unsigned int i = 2; i < CE->getNumArgs(); ++i) State = State->add<TaintArgsOnPostVisit>(i); return State; } return nullptr; }
void IntegerOverflowChecker::checkPostStmt(const CXXNewExpr *NewExpr, CheckerContext &C) const { if (!Filter.CheckIntegerOverflowDef) return; if (NewExpr->getOperatorNew()->getOverloadedOperator() != OO_Array_New) return; const Expr *ArrSize = NewExpr->getArraySize(); SVal ElementCount = C.getSVal(ArrSize); ProgramStateRef State = C.getState(); if (makeGlobalsMembersHeuristics(ElementCount, ArrSize, C)) { C.addTransition(addToWhiteList(ElementCount, State)); return; } QualType NewExprType = NewExpr->getAllocatedType(); uint64_t NewExprTypeSize = C.getASTContext().getTypeSizeInChars(NewExprType) .getQuantity(); SValBuilder &SvalBuilder = C.getSValBuilder(); SVal NewExprTypeSizeVal = SvalBuilder.makeIntVal(NewExprTypeSize, true); bool isOverflow; Optional<DefinedOrUnknownSVal> CondOverflow = checkMul(C, NewExprTypeSizeVal, ElementCount, ArrSize->getType(), isOverflow); if (!CondOverflow) return; ProgramStateRef StateOverflow, StateNotOverflow; std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); if (!StateOverflow || (StateNotOverflow && !State->isTainted(ElementCount))) return; std::string Msg = composeMsg(StateNotOverflow, NewExprTypeSizeVal, ElementCount, 0, ArrSize, false, isOverflow, 0, C); reportBug(Msg, C, NewExpr->getExprLoc(), false); }
void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, const Stmt* LoadS, CheckerContext &checkerContext) const { // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() // for all clients to use. // // The algorithm we are using here for bounds checking is to see if the // memory access is within the extent of the base region. Since we // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. ProgramStateRef state = checkerContext.getState(); ProgramStateRef originalState = state; SValBuilder &svalBuilder = checkerContext.getSValBuilder(); const RegionRawOffsetV2 &rawOffset = RegionRawOffsetV2::computeOffset(state, svalBuilder, location); if (!rawOffset.getRegion()) return; // CHECK LOWER BOUND: Is byteOffset < extent begin? // If so, we are doing a load/store // before the first valid offset in the memory region. SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); if (isa<NonLoc>(extentBegin)) { SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), cast<NonLoc>(extentBegin), svalBuilder.getConditionType()); NonLoc *lowerBoundToCheck = dyn_cast<NonLoc>(&lowerBound); if (!lowerBoundToCheck) return; ProgramStateRef state_precedesLowerBound, state_withinLowerBound; llvm::tie(state_precedesLowerBound, state_withinLowerBound) = state->assume(*lowerBoundToCheck); // Are we constrained enough to definitely precede the lower bound? if (state_precedesLowerBound && !state_withinLowerBound) { reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); return; } // Otherwise, assume the constraint of the lower bound. assert(state_withinLowerBound); state = state_withinLowerBound; } do { // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so, // we are doing a load/store after the last valid offset. DefinedOrUnknownSVal extentVal = rawOffset.getRegion()->getExtent(svalBuilder); if (!isa<NonLoc>(extentVal)) break; SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), cast<NonLoc>(extentVal), svalBuilder.getConditionType()); NonLoc *upperboundToCheck = dyn_cast<NonLoc>(&upperbound); if (!upperboundToCheck) break; ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = state->assume(*upperboundToCheck); // If we are under constrained and the index variables are tainted, report. if (state_exceedsUpperBound && state_withinUpperBound) { if (state->isTainted(rawOffset.getByteOffset())) reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted); return; } // If we are constrained enough to definitely exceed the upper bound, report. if (state_exceedsUpperBound) { assert(!state_withinUpperBound); reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); return; } assert(state_withinUpperBound); state = state_withinUpperBound; } while (false); if (state != originalState) checkerContext.addTransition(state); }
void IntegerOverflowChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { if (OverflowLoc.find(B->getExprLoc()) != OverflowLoc.end()) return; if (!B->getLHS()->getType()->isIntegerType() || !B->getRHS()->getType()->isIntegerType()) return; ProgramStateRef State = C.getState(); QualType BinType = B->getType(); const Expr *ExprLhs = B->getLHS(); const Expr *ExprRhs = B->getRHS(); SVal Lhs = C.getSVal(ExprLhs); SVal Rhs = C.getSVal(ExprRhs); if (makeGlobalsMembersHeuristics(Lhs, ExprLhs, C)) { C.addTransition(addToWhiteList(Lhs, State)); return; } if (makeGlobalsMembersHeuristics(Rhs, ExprRhs, C)) { C.addTransition(addToWhiteList(Rhs, State)); return; } if (!Filter.CheckIntegerOverflowDef && BinType->isUnsignedIntegerType()) return; if (!Filter.CheckIntegerOverflowUndef && BinType->isSignedIntegerType()) return; BinaryOperator::Opcode Op = B->getOpcode(); if (Op != BO_Add && Op != BO_Mul && Op != BO_Sub && Op != BO_AddAssign && Op != BO_MulAssign && Op != BO_SubAssign) return; Optional<DefinedOrUnknownSVal> CondOverflow; ProgramStateRef StateOverflow, StateNotOverflow; bool isOverflow = false; if (Op == BO_Add || Op == BO_AddAssign) CondOverflow = checkAdd(C, Lhs, Rhs, BinType, isOverflow); else if (Op == BO_Sub || Op == BO_SubAssign) { if ((BinType->isUnsignedIntegerType()) && makeUSubHeuristics(B)) return; CondOverflow = checkSub(C, Lhs, Rhs, BinType, isOverflow); } else if (Op == BO_Mul || Op == BO_MulAssign) CondOverflow = checkMul(C, Lhs, Rhs, BinType, isOverflow); if (!CondOverflow) return; std::tie(StateOverflow, StateNotOverflow) = State->assume(*CondOverflow); if (!StateOverflow || (StateNotOverflow && !(State->isTainted(Lhs) || State->isTainted(Rhs)))) return; std::string Msg = composeMsg(StateNotOverflow, Lhs, Rhs, ExprLhs, ExprRhs, B->getType()->isSignedIntegerOrEnumerationType(), isOverflow, &Op, C); reportBug(Msg, C, B->getExprLoc(), BinType->isSignedIntegerType()); }
void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (!DS->isSingleDecl()) return; const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); if (!VD) return; ASTContext &Ctx = C.getASTContext(); const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); if (!VLA) return; // FIXME: Handle multi-dimensional VLAs. const Expr *SE = VLA->getSizeExpr(); ProgramStateRef state = C.getState(); SVal sizeV = state->getSVal(SE, C.getLocationContext()); if (sizeV.isUndef()) { reportBug(VLA_Garbage, SE, state, C); return; } // See if the size value is known. It can't be undefined because we would have // warned about that already. if (sizeV.isUnknown()) return; // Check if the size is tainted. if (state->isTainted(sizeV)) { reportBug(VLA_Tainted, SE, nullptr, C); return; } // Check if the size is zero. DefinedSVal sizeD = sizeV.castAs<DefinedSVal>(); ProgramStateRef stateNotZero, stateZero; std::tie(stateNotZero, stateZero) = state->assume(sizeD); if (stateZero && !stateNotZero) { reportBug(VLA_Zero, SE, stateZero, C); return; } // From this point on, assume that the size is not zero. state = stateNotZero; // VLASizeChecker is responsible for defining the extent of the array being // declared. We do this by multiplying the array length by the element size, // then matching that with the array region's extent symbol. // Convert the array length to size_t. SValBuilder &svalBuilder = C.getSValBuilder(); QualType SizeTy = Ctx.getSizeType(); NonLoc ArrayLength = svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>(); // Get the element size. CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. SVal ArraySizeVal = svalBuilder.evalBinOpNN( state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy); // Finally, assume that the array's extent matches the given size. const LocationContext *LC = C.getLocationContext(); DefinedOrUnknownSVal Extent = state->getRegion(VD, LC)->getExtent(svalBuilder); DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>(); DefinedOrUnknownSVal sizeIsKnown = svalBuilder.evalEQ(state, Extent, ArraySize); state = state->assume(sizeIsKnown, true); // Assume should not fail at this point. assert(state); // Remember our assumptions! C.addTransition(state); }