================ @@ -50,6 +122,107 @@ class BuiltinFunctionChecker : public Checker<eval::Call> { } // namespace +const NoteTag *BuiltinFunctionChecker::createBuiltinNoOverflowNoteTag( + CheckerContext &C, bool bothFeasible, SVal Arg1, SVal Arg2, + SVal Result) const { + return C.getNoteTag([Result, Arg1, Arg2, bothFeasible]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (!BR.isInteresting(Result)) + return; + + // Propagate interestingness to input argumets if result is interesting. + BR.markInteresting(Arg1); + BR.markInteresting(Arg2); + + if (bothFeasible) + OS << "Assuming overflow does not happen"; + }); +} + +const NoteTag * +BuiltinFunctionChecker::createBuiltinOverflowNoteTag(CheckerContext &C) const { + return C.getNoteTag( + [](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + OS << "Assuming overflow does happen"; + }, + /*isPrunable=*/true); +} + +std::pair<bool, bool> +BuiltinFunctionChecker::checkOverflow(CheckerContext &C, SVal RetVal, + QualType Res) const { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ACtx = C.getASTContext(); + + // Calling a builtin with a non-integer type result produces compiler error. + assert(Res->isIntegerType()); + + unsigned BitWidth = ACtx.getIntWidth(Res); + auto MinVal = + llvm::APSInt::getMinValue(BitWidth, Res->isUnsignedIntegerType()); + auto MaxVal = + llvm::APSInt::getMaxValue(BitWidth, Res->isUnsignedIntegerType()); + + SVal IsLeMax = + SVB.evalBinOp(State, BO_LE, RetVal, nonloc::ConcreteInt(MaxVal), Res); + SVal IsGeMin = + SVB.evalBinOp(State, BO_GE, RetVal, nonloc::ConcreteInt(MinVal), Res); + + auto [MayNotOverflow, MayOverflow] = + State->assume(IsLeMax.castAs<DefinedOrUnknownSVal>()); + auto [MayNotUnderflow, MayUnderflow] = + State->assume(IsGeMin.castAs<DefinedOrUnknownSVal>()); + + return {MayOverflow || MayUnderflow, MayNotOverflow && MayNotUnderflow}; +} + +void BuiltinFunctionChecker::handleOverflowBuiltin(const CallEvent &Call, + CheckerContext &C, + BinaryOperator::Opcode Op, + QualType ResultType) const { + // Calling a builtin with an incorrect argument count produces compiler error. + assert(Call.getNumArgs() == 3); + + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + const Expr *CE = Call.getOriginExpr(); + + SVal Arg1 = Call.getArgSVal(0); + SVal Arg2 = Call.getArgSVal(1); + + SVal RetValMax = SVB.evalBinOp(State, Op, Arg1, Arg2, + getSufficientTypeForOverflowOp(C, ResultType)); + SVal RetVal = SVB.evalBinOp(State, Op, Arg1, Arg2, ResultType); + + auto [Overflow, NotOverflow] = checkOverflow(C, RetValMax, ResultType); + if (NotOverflow) { + ProgramStateRef StateNoOverflow = + State->BindExpr(CE, C.getLocationContext(), SVB.makeTruthVal(false)); + + if (auto L = Call.getArgSVal(2).getAs<Loc>()) { + StateNoOverflow = + StateNoOverflow->bindLoc(*L, RetVal, C.getLocationContext()); + + // Propagate taint if any of the argumets were tainted + if (isTainted(State, Arg1) || isTainted(State, Arg2)) + StateNoOverflow = addTaint(StateNoOverflow, *L); + } + + const NoteTag *tag = createBuiltinNoOverflowNoteTag( + C, NotOverflow && Overflow, Arg1, Arg2, RetVal); ---------------- steakhal wrote:
```suggestion C, /*BothFeasible=*/NotOverflow && Overflow, Arg1, Arg2, RetVal); ``` Maybe we could just invoke this fn directly in the `addTransition` call - unless that would break the lines in an ugly way. https://github.com/llvm/llvm-project/pull/102602 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits