Skip to content

[analyzer] Split TaintPropagation checker into reporting and modeling checkers #98157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1003,17 +1003,14 @@ array new C++ operator is tainted (potentially attacker controlled).
If an attacker can inject a large value as the size parameter, memory exhaustion
denial of service attack can be carried out.

The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
this checker to give warnings.

The analyzer emits warning only if it cannot prove that the size parameter is
within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
covers the SEI Cert coding standard rule `INT04-C
<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.

You can silence this warning either by bound checking the ``size`` parameter, or
by explicitly marking the ``size`` parameter as sanitized. See the
:ref:`alpha-security-taint-TaintPropagation` checker for more details.
:ref:`alpha-security-taint-GenericTaint` checker for an example.

.. code-block:: c

Expand Down Expand Up @@ -3011,10 +3008,10 @@ alpha.security.taint
Checkers implementing
`taint analysis <https://en.wikipedia.org/wiki/Taint_checking>`_.

.. _alpha-security-taint-TaintPropagation:
.. _alpha-security-taint-GenericTaint:

alpha.security.taint.TaintPropagation (C, C++)
""""""""""""""""""""""""""""""""""""""""""""""
alpha.security.taint.GenericTaint (C, C++)
""""""""""""""""""""""""""""""""""""""""""

Taint analysis identifies potential security vulnerabilities where the
attacker can inject malicious data to the program to execute an attack
Expand Down
13 changes: 8 additions & 5 deletions clang/docs/analyzer/user-docs/TaintAnalysisConfiguration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
Taint Analysis Configuration
============================

The Clang Static Analyzer uses taint analysis to detect security-related issues in code.
The backbone of taint analysis in the Clang SA is the `GenericTaintChecker`, which the user can access via the :ref:`alpha-security-taint-TaintPropagation` checker alias and this checker has a default taint-related configuration.
The built-in default settings are defined in code, and they are always in effect once the checker is enabled, either directly or via the alias.
The checker also provides a configuration interface for extending the default settings by providing a configuration file in `YAML <http://llvm.org/docs/YamlIO.html#introduction-to-yaml>`_ format.
The Clang Static Analyzer uses taint analysis to detect injection vulnerability related issues in code.
The backbone of taint analysis in the Clang SA is the ``TaintPropagation`` modeling checker.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit strange that the TaintPropagation checker is hidden from the user and e.g. it doesn't have a documentation, but it still mentioned on this page and it still appears in the name of alpha.security.taint.TaintPropagation:Config.

I don't think that this is a blocking issue for this commit, but I think that it would be important to resolve this in the future. I can imagine two main solutions:

  • We double down on the idea that "modeling checkers are hidden internal parts of the analyzer engine", eliminate all references to TaintPropagation and convert alpha.security.taint.TaintPropagation:Config into an option of the analyzer itself.
  • We document the modeling checkers (probably in a separate section, as the alpha and debug checkers are already separate sections within the checker list). This would allow us to have modeling checkers that can be customized/configured or even disabled by the user.

@haoNoQ @Xazax-hun @steakhal What do you think about this? Which direction would be better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I'd prefer accepting modeling checkers as a thing and have configs for them. I'd rather not make this as an option to the analyzer itself.

The reports are emitted via the :ref:`alpha-security-taint-GenericTaint` checker.
The ``TaintPropagation`` checker has a default taint-related configuration.
The built-in default settings are defined in code, and they are always in effect.
The checker also provides a configuration interface for extending the default settings via the ``alpha.security.taint.TaintPropagation:Config`` checker config parameter
by providing a configuration file to the in `YAML <http://llvm.org/docs/YamlIO.html#introduction-to-yaml>`_ format.
This documentation describes the syntax of the configuration file and gives the informal semantics of the configuration options.

.. contents::
Expand All @@ -18,7 +21,7 @@ ________

Taint analysis works by checking for the occurrence of special operations during the symbolic execution of the program.
Taint analysis defines sources, sinks, and propagation rules. It identifies errors by detecting a flow of information that originates from a taint source, reaches a taint sink, and propagates through the program paths via propagation rules.
A source, sink, or an operation that propagates taint is mainly domain-specific knowledge, but there are some built-in defaults provided by :ref:`alpha-security-taint-TaintPropagation`.
A source, sink, or an operation that propagates taint is mainly domain-specific knowledge, but there are some built-in defaults provided by the ``TaintPropagation`` checker.
It is possible to express that a statement sanitizes tainted values by providing a ``Filters`` section in the external configuration (see :ref:`clangsa-taint-configuration-example` and :ref:`clangsa-taint-filter-details`).
There are no default filters defined in the built-in settings.
The checker's documentation also specifies how to provide a custom taint configuration with command-line options.
Expand Down
12 changes: 8 additions & 4 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">,

let ParentPackage = Taint in {

def GenericTaintChecker : Checker<"TaintPropagation">,
def TaintPropagationChecker : Checker<"TaintPropagation">, // Modelling checker
HelpText<"Generate taint information used by other checkers">,
CheckerOptions<[
CmdLineOption<String,
Expand All @@ -1080,6 +1080,12 @@ def GenericTaintChecker : Checker<"TaintPropagation">,
"",
InAlpha>,
]>,
Documentation<NotDocumented>,
Hidden;

def GenericTaintChecker : Checker<"GenericTaint">,
HelpText<"Reports potential injection vulnerabilities">,
Dependencies<[TaintPropagationChecker]>,
Documentation<HasDocumentation>;

} // end "alpha.security.taint"
Expand Down Expand Up @@ -1717,9 +1723,7 @@ let ParentPackage = TaintOptIn in {
def TaintedAllocChecker: Checker<"TaintedAlloc">,
HelpText<"Check for memory allocations, where the size parameter "
"might be a tainted (attacker controlled) value.">,
Dependencies<[DynamicMemoryModeling]>,
//FIXME: GenericTaintChecker should be a dependency, but only after it
//is transformed into a modeling checker
Dependencies<[DynamicMemoryModeling, TaintPropagationChecker]>,
Documentation<HasDocumentation>;

} // end "optin.taint"
Expand Down
28 changes: 22 additions & 6 deletions clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/YAMLTraits.h"

#include <limits>
Expand Down Expand Up @@ -391,9 +392,10 @@ class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
bool generateReportIfTainted(const Expr *E, StringRef Msg,
CheckerContext &C) const;

private:
const BugType BT{this, "Use of Untrusted Data", categories::TaintedData};
bool isTaintReporterCheckerEnabled = false;
std::optional<BugType> BT;

private:
bool checkUncontrolledFormatString(const CallEvent &Call,
CheckerContext &C) const;

Expand Down Expand Up @@ -1033,20 +1035,23 @@ bool GenericTaintRule::UntrustedEnv(CheckerContext &C) {
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
CheckerContext &C) const {
assert(E);
if (!isTaintReporterCheckerEnabled)
return false;
std::optional<SVal> TaintedSVal =
getTaintedPointeeOrPointer(C.getState(), C.getSVal(E));

if (!TaintedSVal)
return false;

// Generate diagnostic.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Perhaps add an assert(BT) here for the sake of paranoia?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are there, maybe we should also assert that inside the register call, before emplacing it was empty.

if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
auto report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
assert(BT);
static CheckerProgramPointTag Tag(BT->getCheckerName(), Msg);
if (ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag)) {
auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
report->addRange(E->getSourceRange());
for (auto TaintedSym : getTaintedSymbols(C.getState(), *TaintedSVal)) {
report->markInteresting(TaintedSym);
}

C.emitReport(std::move(report));
return true;
}
Expand Down Expand Up @@ -1122,10 +1127,21 @@ void GenericTaintChecker::taintUnsafeSocketProtocol(const CallEvent &Call,
}

/// Checker registration
void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
void ento::registerTaintPropagationChecker(CheckerManager &Mgr) {
Mgr.registerChecker<GenericTaintChecker>();
}

bool ento::shouldRegisterTaintPropagationChecker(const CheckerManager &mgr) {
return true;
}

void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
GenericTaintChecker *checker = Mgr.getChecker<GenericTaintChecker>();
checker->isTaintReporterCheckerEnabled = true;
checker->BT.emplace(Mgr.getCurrentCheckerName(), "Use of Untrusted Data",
categories::TaintedData);
}

bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) {
return true;
}
Loading