diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -64,61 +64,79 @@ if (Idx == ElementCount) return; - ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); - if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateErrorNode(StOutBound); - - if (!N) + auto ElementCountNL = ElementCount.getAs(); + assert(ElementCountNL && "Array size should not be a pointer"); + auto IdxNegativeVal = + C.getSValBuilder() + .evalBinOpNN(state, BO_LT, *ElementCountNL, + C.getSValBuilder().makeZeroArrayIndex(), + C.getSValBuilder().getConditionType()) + .castAs(); + ProgramStateRef IdxNegative, IdxNonNegative; + std::tie(IdxNegative, IdxNonNegative) = state->assume(IdxNegativeVal); + + ProgramStateRef StError; + if (IdxNegative && !IdxNonNegative) { + StError = IdxNegative; + } else { + ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); + if (StOutBound && !StInBound) + StError = StOutBound; + else return; + } - // FIXME: This bug correspond to CWE-466. Eventually we should have bug - // types explicitly reference such exploit categories (when applicable). - if (!BT) - BT.reset(new BuiltinBug( - this, "Buffer overflow", - "Returned pointer value points outside the original object " - "(potential buffer overflow)")); - - // Generate a report for this bug. - auto Report = - std::make_unique(*BT, BT->getDescription(), N); - Report->addRange(RetE->getSourceRange()); - - const auto ConcreteElementCount = ElementCount.getAs(); - const auto ConcreteIdx = Idx.getAs(); - - const auto *DeclR = ER->getSuperRegion()->getAs(); - - if (DeclR) - Report->addNote("Original object declared here", - {DeclR->getDecl(), C.getSourceManager()}); - - if (ConcreteElementCount) { - SmallString<128> SBuf; - llvm::raw_svector_ostream OS(SBuf); - OS << "Original object "; - if (DeclR) { - OS << "'"; - DeclR->getDecl()->printName(OS); - OS << "' "; - } - OS << "is an array of " << ConcreteElementCount->getValue() << " '"; - ER->getValueType().print(OS, - PrintingPolicy(C.getASTContext().getLangOpts())); - OS << "' objects"; - if (ConcreteIdx) { - OS << ", returned pointer points at index " << ConcreteIdx->getValue(); - } - - Report->addNote(SBuf, - {RetE, C.getSourceManager(), C.getLocationContext()}); - } + ExplodedNode *N = C.generateErrorNode(StError); - bugreporter::trackExpressionValue(N, RetE, *Report); + if (!N) + return; - C.emitReport(std::move(Report)); + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + if (!BT) + BT.reset(new BuiltinBug( + this, "Buffer overflow", + "Returned pointer value points outside the original object " + "(potential buffer overflow)")); + + // Generate a report for this bug. + auto Report = + std::make_unique(*BT, BT->getDescription(), N); + Report->addRange(RetE->getSourceRange()); + + const auto ConcreteElementCount = ElementCount.getAs(); + const auto ConcreteIdx = Idx.getAs(); + + const auto *DeclR = ER->getSuperRegion()->getAs(); + + if (DeclR) + Report->addNote("Original object declared here", + {DeclR->getDecl(), C.getSourceManager()}); + + if (ConcreteElementCount) { + SmallString<128> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Original object "; + if (DeclR) { + OS << "'"; + DeclR->getDecl()->printName(OS); + OS << "' "; + } + OS << "is an array of " << ConcreteElementCount->getValue() << " '"; + ER->getValueType().print(OS, + PrintingPolicy(C.getASTContext().getLangOpts())); + OS << "' objects"; + if (ConcreteIdx) { + OS << ", returned pointer points at index " << ConcreteIdx->getValue(); + } + + Report->addNote(SBuf, {RetE, C.getSourceManager(), C.getLocationContext()}); } + + bugreporter::trackExpressionValue(N, RetE, *Report); + + C.emitReport(std::move(Report)); } void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) { diff --git a/clang/test/Analysis/return-ptr-range.cpp b/clang/test/Analysis/return-ptr-range.cpp --- a/clang/test/Analysis/return-ptr-range.cpp +++ b/clang/test/Analysis/return-ptr-range.cpp @@ -115,3 +115,24 @@ } +int *test_negative_index(int I) { + static int arr[10]; // expected-note{{Original object declared here}} + // expected-note@-1{{'arr' initialized here}} + // expected-note@-2{{Original object declared here}} + // expected-note@-3{{'arr' initialized here}} + if (I == -1) // expected-note{{Assuming the condition is true}} + // expected-note@-1{{Taking true branch}} + // expected-note@-2{{Assuming the condition is false}} + // expected-note@-3{{Taking false branch}} + return arr + I; // expected-warning{{Returned pointer value points outside the original object}} + // expected-note@-1{{Returned pointer value points outside the original object}} + // expected-note@-2{{Original object 'arr' is an array of 10 'int' objects, returned pointer points at index -1}} + if (I == -2) // expected-note{{Assuming the condition is true}} + // expected-note@-1{{Taking true branch}} + return arr + I; // expected-warning{{Returned pointer value points outside the original object}} + // expected-note@-1{{Returned pointer value points outside the original object}} + // expected-note@-2{{Original object 'arr' is an array of 10 'int' objects, returned pointer points at index -2}} + if (I == 0) + return arr + I; + return arr + I; +}