
We have a new policy in place making links to private resources something we try to avoid in source and test files. Normally, we'd organically switch to the new policy rather than make a sweeping change across a project. However, Clang is in a somewhat special circumstance currently: recently, I've had several new contributors run into rdar links around test code which their patch was changing the behavior of. This turns out to be a surprisingly bad experience, especially for newer folks, for a handful of reasons: not understanding what the link is and feeling intimidated by it, wondering whether their changes are actually breaking something important to a downstream in some way, having to hunt down strangers not involved with the patch to impose on them for help, accidental pressure from asking for potentially private IP to be made public, etc. Because folks run into these links entirely by chance (through fixing bugs or working on new features), there's not really a set of problematic links to focus on -- all of the links have basically the same potential for causing these problems. As a result, this is an omnibus patch to remove all such links. This was not a mechanical change; it was done by manually searching for rdar, radar, radr, and other variants to find all the various problematic links. From there, I tried to retain or reword the surrounding comments so that we would lose as little context as possible. However, because most links were just a plain link with no supporting context, the majority of the changes are simple removals. Differential Review: https://reviews.llvm.org/D158071
191 lines
7.1 KiB
C++
191 lines
7.1 KiB
C++
//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This defines UndefResultChecker, a builtin check in ExprEngine that
|
|
// performs checks for undefined results of non-assignment binary operators.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
|
|
namespace {
|
|
class UndefResultChecker
|
|
: public Checker< check::PostStmt<BinaryOperator> > {
|
|
|
|
mutable std::unique_ptr<BugType> BT;
|
|
|
|
public:
|
|
void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
|
|
ProgramStateRef state = C.getState();
|
|
|
|
if (!isa<ArraySubscriptExpr>(Ex))
|
|
return false;
|
|
|
|
SVal Loc = C.getSVal(Ex);
|
|
if (!Loc.isValid())
|
|
return false;
|
|
|
|
const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion();
|
|
const ElementRegion *ER = dyn_cast<ElementRegion>(MR);
|
|
if (!ER)
|
|
return false;
|
|
|
|
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
|
|
DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
|
|
state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
|
|
ProgramStateRef StInBound, StOutBound;
|
|
std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
|
|
return StOutBound && !StInBound;
|
|
}
|
|
|
|
static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
|
|
return C.isGreaterOrEqual(
|
|
B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
|
|
}
|
|
|
|
static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
|
|
CheckerContext &C) {
|
|
SValBuilder &SB = C.getSValBuilder();
|
|
ProgramStateRef State = C.getState();
|
|
const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
|
|
const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
|
|
assert(LHS && RHS && "Values unknown, inconsistent state");
|
|
return (unsigned)RHS->getZExtValue() > LHS->countl_zero();
|
|
}
|
|
|
|
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
|
|
CheckerContext &C) const {
|
|
if (C.getSVal(B).isUndef()) {
|
|
|
|
// Do not report assignments of uninitialized values inside swap functions.
|
|
// This should allow to swap partially uninitialized structs
|
|
if (const FunctionDecl *EnclosingFunctionDecl =
|
|
dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
|
|
if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
|
|
return;
|
|
|
|
// Generate an error node.
|
|
ExplodedNode *N = C.generateErrorNode();
|
|
if (!N)
|
|
return;
|
|
|
|
if (!BT)
|
|
BT.reset(
|
|
new BugType(this, "Result of operation is garbage or undefined"));
|
|
|
|
SmallString<256> sbuf;
|
|
llvm::raw_svector_ostream OS(sbuf);
|
|
const Expr *Ex = nullptr;
|
|
bool isLeft = true;
|
|
|
|
if (C.getSVal(B->getLHS()).isUndef()) {
|
|
Ex = B->getLHS()->IgnoreParenCasts();
|
|
isLeft = true;
|
|
}
|
|
else if (C.getSVal(B->getRHS()).isUndef()) {
|
|
Ex = B->getRHS()->IgnoreParenCasts();
|
|
isLeft = false;
|
|
}
|
|
|
|
if (Ex) {
|
|
OS << "The " << (isLeft ? "left" : "right") << " operand of '"
|
|
<< BinaryOperator::getOpcodeStr(B->getOpcode())
|
|
<< "' is a garbage value";
|
|
if (isArrayIndexOutOfBounds(C, Ex))
|
|
OS << " due to array index out of bounds";
|
|
} else {
|
|
// Neither operand was undefined, but the result is undefined.
|
|
if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
|
|
B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
|
|
C.isNegative(B->getRHS())) {
|
|
OS << "The result of the "
|
|
<< ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
|
|
: "right")
|
|
<< " shift is undefined because the right operand is negative";
|
|
Ex = B->getRHS();
|
|
} else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
|
|
B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
|
|
isShiftOverflow(B, C)) {
|
|
|
|
OS << "The result of the "
|
|
<< ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
|
|
: "right")
|
|
<< " shift is undefined due to shifting by ";
|
|
Ex = B->getRHS();
|
|
|
|
SValBuilder &SB = C.getSValBuilder();
|
|
const llvm::APSInt *I =
|
|
SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
|
|
if (!I)
|
|
OS << "a value that is";
|
|
else if (I->isUnsigned())
|
|
OS << '\'' << I->getZExtValue() << "\', which is";
|
|
else
|
|
OS << '\'' << I->getSExtValue() << "\', which is";
|
|
|
|
OS << " greater or equal to the width of type '"
|
|
<< B->getLHS()->getType() << "'.";
|
|
} else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
|
|
C.isNegative(B->getLHS())) {
|
|
OS << "The result of the left shift is undefined because the left "
|
|
"operand is negative";
|
|
Ex = B->getLHS();
|
|
} else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
|
|
isLeftShiftResultUnrepresentable(B, C)) {
|
|
ProgramStateRef State = C.getState();
|
|
SValBuilder &SB = C.getSValBuilder();
|
|
const llvm::APSInt *LHS =
|
|
SB.getKnownValue(State, C.getSVal(B->getLHS()));
|
|
const llvm::APSInt *RHS =
|
|
SB.getKnownValue(State, C.getSVal(B->getRHS()));
|
|
OS << "The result of the left shift is undefined due to shifting \'"
|
|
<< LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
|
|
<< "\', which is unrepresentable in the unsigned version of "
|
|
<< "the return type \'" << B->getLHS()->getType() << "\'";
|
|
Ex = B->getLHS();
|
|
} else {
|
|
OS << "The result of the '"
|
|
<< BinaryOperator::getOpcodeStr(B->getOpcode())
|
|
<< "' expression is undefined";
|
|
}
|
|
}
|
|
auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
|
|
if (Ex) {
|
|
report->addRange(Ex->getSourceRange());
|
|
bugreporter::trackExpressionValue(N, Ex, *report);
|
|
}
|
|
else
|
|
bugreporter::trackExpressionValue(N, B, *report);
|
|
|
|
C.emitReport(std::move(report));
|
|
}
|
|
}
|
|
|
|
void ento::registerUndefResultChecker(CheckerManager &mgr) {
|
|
mgr.registerChecker<UndefResultChecker>();
|
|
}
|
|
|
|
bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) {
|
|
return true;
|
|
}
|