|  | //=== Taint.cpp - Taint tracking and basic propagation rules. ------*- 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Defines basic, non-domain-specific mechanisms for tracking tainted values. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/StaticAnalyzer/Checkers/Taint.h" | 
|  | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" | 
|  | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | 
|  | #include <optional> | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace ento; | 
|  | using namespace taint; | 
|  |  | 
|  | // Fully tainted symbols. | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(TaintMap, SymbolRef, TaintTagType) | 
|  |  | 
|  | // Partially tainted symbols. | 
|  | REGISTER_MAP_FACTORY_WITH_PROGRAMSTATE(TaintedSubRegions, const SubRegion *, | 
|  | TaintTagType) | 
|  | REGISTER_MAP_WITH_PROGRAMSTATE(DerivedSymTaint, SymbolRef, TaintedSubRegions) | 
|  |  | 
|  | void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL, | 
|  | const char *Sep) { | 
|  | TaintMapTy TM = State->get<TaintMap>(); | 
|  |  | 
|  | if (!TM.isEmpty()) | 
|  | Out << "Tainted symbols:" << NL; | 
|  |  | 
|  | for (const auto &I : TM) | 
|  | Out << I.first << " : " << I.second << NL; | 
|  | } | 
|  |  | 
|  | void taint::dumpTaint(ProgramStateRef State) { | 
|  | printTaint(State, llvm::errs()); | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S, | 
|  | const LocationContext *LCtx, | 
|  | TaintTagType Kind) { | 
|  | return addTaint(State, State->getSVal(S, LCtx), Kind); | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, | 
|  | TaintTagType Kind) { | 
|  | SymbolRef Sym = V.getAsSymbol(); | 
|  | if (Sym) | 
|  | return addTaint(State, Sym, Kind); | 
|  |  | 
|  | // If the SVal represents a structure, try to mass-taint all values within the | 
|  | // structure. For now it only works efficiently on lazy compound values that | 
|  | // were conjured during a conservative evaluation of a function - either as | 
|  | // return values of functions that return structures or arrays by value, or as | 
|  | // values of structures or arrays passed into the function by reference, | 
|  | // directly or through pointer aliasing. Such lazy compound values are | 
|  | // characterized by having exactly one binding in their captured store within | 
|  | // their parent region, which is a conjured symbol default-bound to the base | 
|  | // region of the parent region. | 
|  | if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { | 
|  | if (std::optional<SVal> binding = | 
|  | State->getStateManager().getStoreManager().getDefaultBinding( | 
|  | *LCV)) { | 
|  | if (SymbolRef Sym = binding->getAsSymbol()) | 
|  | return addPartialTaint(State, Sym, LCV->getRegion(), Kind); | 
|  | } | 
|  | } | 
|  |  | 
|  | const MemRegion *R = V.getAsRegion(); | 
|  | return addTaint(State, R, Kind); | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::addTaint(ProgramStateRef State, const MemRegion *R, | 
|  | TaintTagType Kind) { | 
|  | if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) | 
|  | return addTaint(State, SR->getSymbol(), Kind); | 
|  | return State; | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::addTaint(ProgramStateRef State, SymbolRef Sym, | 
|  | TaintTagType Kind) { | 
|  | // If this is a symbol cast, remove the cast before adding the taint. Taint | 
|  | // is cast agnostic. | 
|  | while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) | 
|  | Sym = SC->getOperand(); | 
|  |  | 
|  | ProgramStateRef NewState = State->set<TaintMap>(Sym, Kind); | 
|  | assert(NewState); | 
|  | return NewState; | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::removeTaint(ProgramStateRef State, SVal V) { | 
|  | SymbolRef Sym = V.getAsSymbol(); | 
|  | if (Sym) | 
|  | return removeTaint(State, Sym); | 
|  |  | 
|  | const MemRegion *R = V.getAsRegion(); | 
|  | return removeTaint(State, R); | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::removeTaint(ProgramStateRef State, const MemRegion *R) { | 
|  | if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) | 
|  | return removeTaint(State, SR->getSymbol()); | 
|  | return State; | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::removeTaint(ProgramStateRef State, SymbolRef Sym) { | 
|  | // If this is a symbol cast, remove the cast before adding the taint. Taint | 
|  | // is cast agnostic. | 
|  | while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) | 
|  | Sym = SC->getOperand(); | 
|  |  | 
|  | ProgramStateRef NewState = State->remove<TaintMap>(Sym); | 
|  | assert(NewState); | 
|  | return NewState; | 
|  | } | 
|  |  | 
|  | ProgramStateRef taint::addPartialTaint(ProgramStateRef State, | 
|  | SymbolRef ParentSym, | 
|  | const SubRegion *SubRegion, | 
|  | TaintTagType Kind) { | 
|  | // Ignore partial taint if the entire parent symbol is already tainted. | 
|  | if (const TaintTagType *T = State->get<TaintMap>(ParentSym)) | 
|  | if (*T == Kind) | 
|  | return State; | 
|  |  | 
|  | // Partial taint applies if only a portion of the symbol is tainted. | 
|  | if (SubRegion == SubRegion->getBaseRegion()) | 
|  | return addTaint(State, ParentSym, Kind); | 
|  |  | 
|  | const TaintedSubRegions *SavedRegs = State->get<DerivedSymTaint>(ParentSym); | 
|  | TaintedSubRegions::Factory &F = State->get_context<TaintedSubRegions>(); | 
|  | TaintedSubRegions Regs = SavedRegs ? *SavedRegs : F.getEmptyMap(); | 
|  |  | 
|  | Regs = F.add(Regs, SubRegion, Kind); | 
|  | ProgramStateRef NewState = State->set<DerivedSymTaint>(ParentSym, Regs); | 
|  | assert(NewState); | 
|  | return NewState; | 
|  | } | 
|  |  | 
|  | bool taint::isTainted(ProgramStateRef State, const Stmt *S, | 
|  | const LocationContext *LCtx, TaintTagType Kind) { | 
|  | return !getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/true) | 
|  | .empty(); | 
|  | } | 
|  |  | 
|  | bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) { | 
|  | return !getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/true) | 
|  | .empty(); | 
|  | } | 
|  |  | 
|  | bool taint::isTainted(ProgramStateRef State, const MemRegion *Reg, | 
|  | TaintTagType K) { | 
|  | return !getTaintedSymbolsImpl(State, Reg, K, /*ReturnFirstOnly=*/true) | 
|  | .empty(); | 
|  | } | 
|  |  | 
|  | bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { | 
|  | return !getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/true) | 
|  | .empty(); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, | 
|  | const Stmt *S, | 
|  | const LocationContext *LCtx, | 
|  | TaintTagType Kind) { | 
|  | return getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/false); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, SVal V, | 
|  | TaintTagType Kind) { | 
|  | return getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/false); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, | 
|  | SymbolRef Sym, | 
|  | TaintTagType Kind) { | 
|  | return getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/false); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, | 
|  | const MemRegion *Reg, | 
|  | TaintTagType Kind) { | 
|  | return getTaintedSymbolsImpl(State, Reg, Kind, /*ReturnFirstOnly=*/false); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, | 
|  | const Stmt *S, | 
|  | const LocationContext *LCtx, | 
|  | TaintTagType Kind, | 
|  | bool returnFirstOnly) { | 
|  | SVal val = State->getSVal(S, LCtx); | 
|  | return getTaintedSymbolsImpl(State, val, Kind, returnFirstOnly); | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, | 
|  | SVal V, TaintTagType Kind, | 
|  | bool returnFirstOnly) { | 
|  | if (SymbolRef Sym = V.getAsSymbol()) | 
|  | return getTaintedSymbolsImpl(State, Sym, Kind, returnFirstOnly); | 
|  | if (const MemRegion *Reg = V.getAsRegion()) | 
|  | return getTaintedSymbolsImpl(State, Reg, Kind, returnFirstOnly); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, | 
|  | const MemRegion *Reg, | 
|  | TaintTagType K, | 
|  | bool returnFirstOnly) { | 
|  | std::vector<SymbolRef> TaintedSymbols; | 
|  | if (!Reg) | 
|  | return TaintedSymbols; | 
|  |  | 
|  | // Element region (array element) is tainted if the offset is tainted. | 
|  | if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) { | 
|  | std::vector<SymbolRef> TaintedIndex = | 
|  | getTaintedSymbolsImpl(State, ER->getIndex(), K, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedIndex); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  |  | 
|  | // Symbolic region is tainted if the corresponding symbol is tainted. | 
|  | if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { | 
|  | std::vector<SymbolRef> TaintedRegions = | 
|  | getTaintedSymbolsImpl(State, SR->getSymbol(), K, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedRegions); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  |  | 
|  | // Any subregion (including Element and Symbolic regions) is tainted if its | 
|  | // super-region is tainted. | 
|  | if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) { | 
|  | std::vector<SymbolRef> TaintedSubRegions = | 
|  | getTaintedSymbolsImpl(State, ER->getSuperRegion(), K, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedSubRegions); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  |  | 
|  | return TaintedSymbols; | 
|  | } | 
|  |  | 
|  | std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, | 
|  | SymbolRef Sym, | 
|  | TaintTagType Kind, | 
|  | bool returnFirstOnly) { | 
|  | std::vector<SymbolRef> TaintedSymbols; | 
|  | if (!Sym) | 
|  | return TaintedSymbols; | 
|  |  | 
|  | // Traverse all the symbols this symbol depends on to see if any are tainted. | 
|  | for (SymbolRef SubSym : Sym->symbols()) { | 
|  | if (!isa<SymbolData>(SubSym)) | 
|  | continue; | 
|  |  | 
|  | if (const TaintTagType *Tag = State->get<TaintMap>(SubSym)) { | 
|  | if (*Tag == Kind) { | 
|  | TaintedSymbols.push_back(SubSym); | 
|  | if (returnFirstOnly) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  | } | 
|  |  | 
|  | if (const auto *SD = dyn_cast<SymbolDerived>(SubSym)) { | 
|  | // If this is a SymbolDerived with a tainted parent, it's also tainted. | 
|  | std::vector<SymbolRef> TaintedParents = getTaintedSymbolsImpl( | 
|  | State, SD->getParentSymbol(), Kind, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedParents); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  |  | 
|  | // If this is a SymbolDerived with the same parent symbol as another | 
|  | // tainted SymbolDerived and a region that's a sub-region of that | 
|  | // tainted symbol, it's also tainted. | 
|  | if (const TaintedSubRegions *Regs = | 
|  | State->get<DerivedSymTaint>(SD->getParentSymbol())) { | 
|  | const TypedValueRegion *R = SD->getRegion(); | 
|  | for (auto I : *Regs) { | 
|  | // FIXME: The logic to identify tainted regions could be more | 
|  | // complete. For example, this would not currently identify | 
|  | // overlapping fields in a union as tainted. To identify this we can | 
|  | // check for overlapping/nested byte offsets. | 
|  | if (Kind == I.second && R->isSubRegionOf(I.first)) { | 
|  | TaintedSymbols.push_back(SD->getParentSymbol()); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // If memory region is tainted, data is also tainted. | 
|  | if (const auto *SRV = dyn_cast<SymbolRegionValue>(SubSym)) { | 
|  | std::vector<SymbolRef> TaintedRegions = | 
|  | getTaintedSymbolsImpl(State, SRV->getRegion(), Kind, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedRegions); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  |  | 
|  | // If this is a SymbolCast from a tainted value, it's also tainted. | 
|  | if (const auto *SC = dyn_cast<SymbolCast>(SubSym)) { | 
|  | std::vector<SymbolRef> TaintedCasts = | 
|  | getTaintedSymbolsImpl(State, SC->getOperand(), Kind, returnFirstOnly); | 
|  | llvm::append_range(TaintedSymbols, TaintedCasts); | 
|  | if (returnFirstOnly && !TaintedSymbols.empty()) | 
|  | return TaintedSymbols; // return early if needed | 
|  | } | 
|  | } | 
|  | return TaintedSymbols; | 
|  | } |