Skip to content
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

Sparse IDE #714

Open
wants to merge 20 commits into
base: development
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Checks: '-*,
-readability-convert-member-functions-to-static,
-readability-isolate-declaration,
-readability-identifier-length,
-readability-redundant-member-init,
-readability-use-anyofallof,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE)
# https://reviews.llvm.org/D157613

string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELEASE "")

Expand Down
13 changes: 12 additions & 1 deletion cmake/phasar_macros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,21 @@ function(generate_ll_file)
endif()

if(GEN_LL_MEM2REG)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
get_filename_component(COMPILER_PATH_STR ${CMAKE_CXX_COMPILER} DIRECTORY)
find_program(OPT_TOOL opt HINTS ${COMPILER_PATH_STR})
else()
find_program(OPT_TOOL opt)
endif()

if(NOT OPT_TOOL)
set(OPT_TOOL opt)
endif()

add_custom_command(
OUTPUT ${test_code_ll_file}
COMMAND ${GEN_CMD} ${test_code_file_path} -o ${test_code_ll_file}
COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} opt -mem2reg -S -opaque-pointers=0 ${test_code_ll_file} -o ${test_code_ll_file}
COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} ${OPT_TOOL} -mem2reg -S -opaque-pointers=0 ${test_code_ll_file} -o ${test_code_ll_file}
COMMENT ${GEN_CMD_COMMENT}
DEPENDS ${GEN_LL_FILE}
VERBATIM
Expand Down
28 changes: 28 additions & 0 deletions config/double-free-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "double-free",
"version": 1.0,
"functions": [
{
"name": "free",
"params": {
"source": [
0
],
"sink": [
0
]
}
},
{
"name": "_ZdlPv",
"params": {
"source": [
0
],
"sink": [
0
]
}
}
]
}
2 changes: 1 addition & 1 deletion include/phasar/ControlFlow/CFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ template <typename Derived> class CFGBase {
return self().getAsJsonImpl(Fun);
}

private:
protected:
Derived &self() noexcept { return static_cast<Derived &>(*this); }
const Derived &self() const noexcept {
return static_cast<const Derived &>(*this);
Expand Down
43 changes: 43 additions & 0 deletions include/phasar/ControlFlow/SparseCFGBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/******************************************************************************
* Copyright (c) 2024 Fabian Schiebel.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of LICENSE.txt.
*
* Contributors:
* Fabian Schiebel and others
*****************************************************************************/

#ifndef PHASAR_CONTROLFLOW_SPARSECFGBASE_H
#define PHASAR_CONTROLFLOW_SPARSECFGBASE_H

#include "phasar/ControlFlow/CFGBase.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/Nullable.h"

namespace psr {
template <typename Derived> class SparseCFGBase : public CFGBase<Derived> {
public:
using typename CFGBase<Derived>::n_t;
using typename CFGBase<Derived>::f_t;

/// Gets the next instruction in control-flow order, starting from
/// FromInstruction, that may use or define Val.
/// If the next user is ambiguous, returns null.
[[nodiscard]] Nullable<n_t>
nextUserOrNull(ByConstRef<n_t> FromInstruction) const {
return self().nextUserOrNullImpl(FromInstruction);
}

protected:
using CFGBase<Derived>::self;
};

template <typename ICF, typename Domain>
// NOLINTNEXTLINE(readability-identifier-naming)
constexpr bool is_sparse_cfg_v = is_crtp_base_of_v<SparseCFGBase, ICF>
&&std::is_same_v<typename ICF::n_t, typename Domain::n_t>
&&std::is_same_v<typename ICF::f_t, typename Domain::f_t>;

} // namespace psr

#endif // PHASAR_CONTROLFLOW_SPARSECFGBASE_H
54 changes: 54 additions & 0 deletions include/phasar/ControlFlow/SparseCFGProvider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/******************************************************************************
* Copyright (c) 2024 Fabian Schiebel.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of LICENSE.txt.
*
* Contributors:
* Fabian Schiebel and others
*****************************************************************************/

#ifndef PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H
#define PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H

#include "phasar/Utils/ByRef.h"

#include <type_traits>

namespace psr {
template <typename T> T valueOf(T Val) { return Val; }

template <typename Derived, typename F, typename V> class SparseCFGProvider {
public:
using f_t = F;
using v_t = V;

template <typename D>
[[nodiscard]] decltype(auto) getSparseCFG(ByConstRef<f_t> Fun,
const D &Val) const {
using psr::valueOf;
static_assert(std::is_convertible_v<decltype(valueOf(Val)), v_t>);
return self().getSparseCFGImpl(Fun, valueOf(Val));
}

private:
Derived &self() noexcept { return static_cast<Derived &>(*this); }
const Derived &self() const noexcept {
return static_cast<const Derived &>(*this);
}
};

template <typename T, typename D, typename = void>
struct has_getSparseCFG : std::false_type {}; // NOLINT
template <typename T, typename D>
struct has_getSparseCFG<
T, D,
std::void_t<decltype(std::declval<const T>().getSparseCFG(
std::declval<typename T::f_t>(), std::declval<D>()))>>
: std::true_type {};

template <typename T, typename D>
// NOLINTNEXTLINE
static constexpr bool has_getSparseCFG_v = has_getSparseCFG<T, D>::value;
} // namespace psr

#endif // PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H
3 changes: 2 additions & 1 deletion include/phasar/DataFlow/IfdsIde/FlowFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H
#define PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H

#include "phasar/Utils/Macros.h"
#include "phasar/Utils/TypeTraits.h"

#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -131,7 +132,7 @@ Container makeContainer(Range &&Rng) {
Container C;
reserveIfPossible(C, Rng.size());
for (auto &&Fact : Rng) {
C.insert(std::forward<decltype(Fact)>(Fact));
C.insert(PSR_FWD(Fact));
}
return C;
}
Expand Down
83 changes: 73 additions & 10 deletions include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVER_H

#include "phasar/Config/Configuration.h"
#include "phasar/ControlFlow/SparseCFGProvider.h"
#include "phasar/DB/ProjectIRDBBase.h"
#include "phasar/DataFlow/IfdsIde/EdgeFunction.h"
#include "phasar/DataFlow/IfdsIde/EdgeFunctionStats.h"
Expand All @@ -35,15 +36,19 @@
#include "phasar/DataFlow/IfdsIde/SolverResults.h"
#include "phasar/Domain/AnalysisDomain.h"
#include "phasar/Utils/Average.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/DOTGraph.h"
#include "phasar/Utils/JoinLattice.h"
#include "phasar/Utils/Logger.h"
#include "phasar/Utils/Macros.h"
#include "phasar/Utils/Nullable.h"
#include "phasar/Utils/PAMMMacros.h"
#include "phasar/Utils/Table.h"
#include "phasar/Utils/Utilities.h"

#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/TypeName.h"
#include "llvm/Support/raw_ostream.h"

#include "nlohmann/json.hpp"
Expand Down Expand Up @@ -81,14 +86,24 @@ class IDESolver
using t_t = typename AnalysisDomainTy::t_t;
using v_t = typename AnalysisDomainTy::v_t;

template <typename I>
IDESolver(IDETabulationProblem<AnalysisDomainTy, Container> &Problem,
const i_t *ICF)
: IDEProblem(Problem), ZeroValue(Problem.getZeroValue()), ICF(ICF),
const I *ICF)
: IDEProblem(Problem), ZeroValue(Problem.getZeroValue()),
ICF(&static_cast<const i_t &>(*ICF)), SVFG(ICF),
SolverConfig(Problem.getIFDSIDESolverConfig()),
CachedFlowEdgeFunctions(Problem), AllTop(Problem.allTopFunction()),
JumpFn(std::make_shared<JumpFunctions<AnalysisDomainTy, Container>>()),
Seeds(Problem.initialSeeds()) {
assert(ICF != nullptr);

if constexpr (has_getSparseCFG_v<I, d_t>) {
NextUserOrNullCB = [](const void *SVFG, ByConstRef<f_t> Fun,
ByConstRef<d_t> d3, ByConstRef<n_t> n) {
auto &&SCFG = static_cast<const I *>(SVFG)->getSparseCFG(Fun, d3);
return SCFG.nextUserOrNull(n);
};
}
}

IDESolver(IDETabulationProblem<AnalysisDomainTy, Container> *Problem,
Expand Down Expand Up @@ -339,6 +354,15 @@ class IDESolver
}

protected:
Nullable<n_t> getNextUserOrNull(ByConstRef<f_t> Fun, ByConstRef<d_t> d3,
ByConstRef<n_t> n) {
if (!NextUserOrNullCB || IDEProblem.isZeroValue(d3)) {
return {};
}

return NextUserOrNullCB(SVFG, Fun, d3, n);
}

/// Lines 13-20 of the algorithm; processing a call site in the caller's
/// context.
///
Expand Down Expand Up @@ -382,6 +406,15 @@ class IDESolver

bool HasNoCalleeInformation = true;

auto &&Fun = ICF->getFunctionOf(n);
auto GetNextUse = [this, &Fun, &n](n_t nPrime, ByConstRef<d_t> d3) {
if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) {
return psr::unwrapNullable(PSR_FWD(NextUser));
}

return nPrime;
};

// for each possible callee
for (f_t SCalledProcN : Callees) { // still line 14
// check if a special summary for the called procedure exists
Expand Down Expand Up @@ -409,7 +442,9 @@ class IDESolver
"Queried Summary Edge Function: " << SumEdgFnE);
PHASAR_LOG_LEVEL(DEBUG,
"Compose: " << SumEdgFnE << " * " << f << '\n');
WorkList.emplace_back(PathEdge(d1, ReturnSiteN, std::move(d3)),

auto DestN = GetNextUse(ReturnSiteN, d3);
WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)),
IDEProblem.extend(f, SumEdgFnE));
}
}
Expand Down Expand Up @@ -508,8 +543,10 @@ class IDESolver
d_t d5_restoredCtx = restoreContextOnReturnedFact(n, d2, d5);
// propagte the effects of the entire call
PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f);

auto DestN = GetNextUse(RetSiteN, d5_restoredCtx);
WorkList.emplace_back(
PathEdge(d1, RetSiteN, std::move(d5_restoredCtx)),
PathEdge(d1, DestN, std::move(d5_restoredCtx)),
IDEProblem.extend(f, fPrime));
}
}
Expand Down Expand Up @@ -545,7 +582,8 @@ class IDESolver
auto fPrime = IDEProblem.extend(f, EdgeFnE);
PHASAR_LOG_LEVEL(DEBUG, "Compose: " << EdgeFnE << " * " << f << " = "
<< fPrime);
WorkList.emplace_back(PathEdge(d1, ReturnSiteN, std::move(d3)),
auto DestN = GetNextUse(ReturnSiteN, d3);
WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)),
std::move(fPrime));
}
}
Expand All @@ -563,6 +601,8 @@ class IDESolver
EdgeFunction<l_t> f = jumpFunction(Edge);
auto [d1, n, d2] = Edge.consume();

const auto &Fun = ICF->getFunctionOf(n);

for (const auto nPrime : ICF->getSuccsOf(n)) {
FlowFunctionPtrType FlowFunc =
CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime);
Expand All @@ -575,14 +615,23 @@ class IDESolver
CachedFlowEdgeFunctions.getNormalEdgeFunction(n, d2, nPrime, d3);
PHASAR_LOG_LEVEL(DEBUG, "Queried Normal Edge Function: " << g);
EdgeFunction<l_t> fPrime = IDEProblem.extend(f, g);

auto DestN = [&, &n = n] {
if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) {
return psr::unwrapNullable(PSR_FWD(NextUser));
}

return nPrime;
}();

if (SolverConfig.emitESG()) {
IntermediateEdgeFunctions[std::make_tuple(n, d2, nPrime, d3)]
IntermediateEdgeFunctions[std::make_tuple(n, d2, DestN, d3)]
.push_back(g);
}
PHASAR_LOG_LEVEL(DEBUG,
"Compose: " << g << " * " << f << " = " << fPrime);
INC_COUNTER("EF Queries", 1, Full);
WorkList.emplace_back(PathEdge(d1, nPrime, std::move(d3)),
WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)),
std::move(fPrime));
}
}
Expand Down Expand Up @@ -915,6 +964,7 @@ class IDESolver
for (const auto &Entry : Inc) {
// line 22
n_t c = Entry.first;
auto &&Fun = ICF->getFunctionOf(c);
// for each return site
for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(c)) {
// compute return-flow function
Expand Down Expand Up @@ -968,9 +1018,19 @@ class IDESolver
d_t d3 = ValAndFunc.first;
d_t d5_restoredCtx = restoreContextOnReturnedFact(c, d4, d5);
PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f3);
WorkList.emplace_back(PathEdge(std::move(d3), RetSiteC,
std::move(d5_restoredCtx)),
IDEProblem.extend(f3, fPrime));

auto DestN = [&] {
if (auto &&NextUser =
getNextUserOrNull(Fun, d5_restoredCtx, c)) {
return psr::unwrapNullable(PSR_FWD(NextUser));
}

return RetSiteC;
}();

WorkList.emplace_back(
PathEdge(std::move(d3), DestN, std::move(d5_restoredCtx)),
IDEProblem.extend(f3, fPrime));
}
}
}
Expand Down Expand Up @@ -1809,7 +1869,10 @@ class IDESolver
IDETabulationProblem<AnalysisDomainTy, Container> &IDEProblem;
d_t ZeroValue;
const i_t *ICF;
const void *SVFG;
IFDSIDESolverConfig &SolverConfig;
Nullable<n_t> (*NextUserOrNullCB)(const void *, ByConstRef<f_t>,
ByConstRef<d_t>, ByConstRef<n_t>) = nullptr;

std::vector<std::pair<PathEdge<n_t, d_t>, EdgeFunction<l_t>>> WorkList;
std::vector<std::pair<n_t, d_t>> ValuePropWL;
Expand Down
Loading