Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
338fd14
build: register extracted analysis sources
SizzleUnrlsd Feb 7, 2026
b4c982f
feat: add size-minus-one write error code
SizzleUnrlsd Feb 7, 2026
270cdc5
refactor: reduce stack analyzer to orchestration glue
SizzleUnrlsd Feb 7, 2026
a1d43c9
test: relax escape expectations for vla read
SizzleUnrlsd Feb 7, 2026
d9f520f
refactor: add input pipeline header
SizzleUnrlsd Feb 7, 2026
6d3c1c9
refactor: add input pipeline header
SizzleUnrlsd Feb 7, 2026
9ec8611
refactor: add analyzer utilities header
SizzleUnrlsd Feb 7, 2026
f11ca93
refactor: add mem intrinsic overflow analysis header
SizzleUnrlsd Feb 7, 2026
a5e0f18
refactor: add const-parameter analysis header
SizzleUnrlsd Feb 7, 2026
c8714a3
refactor: add size-minus-k analysis header
SizzleUnrlsd Feb 7, 2026
a999443
refactor: add integer range analysis header
SizzleUnrlsd Feb 7, 2026
7b63b6f
refactor: add stack computation header
SizzleUnrlsd Feb 7, 2026
a8e4e9c
refactor: add stack pointer escape analysis header
SizzleUnrlsd Feb 7, 2026
a5ebdbb
refactor: add invalid base reconstruction header
SizzleUnrlsd Feb 7, 2026
3397a80
refactor: add stack buffer analysis header
SizzleUnrlsd Feb 7, 2026
401c45b
refactor: add ir value utilities header
SizzleUnrlsd Feb 7, 2026
8cfb399
refactor: add function filter header
SizzleUnrlsd Feb 7, 2026
b95047b
refactor: add alloca usage analysis header
SizzleUnrlsd Feb 7, 2026
a140977
refactor: add module passes header
SizzleUnrlsd Feb 7, 2026
c740407
refactor: move function filter implementation
SizzleUnrlsd Feb 7, 2026
85c1ef1
refactor: move alloca usage analysis
SizzleUnrlsd Feb 7, 2026
0cbe772
refactor: move stack pointer escape analysis
SizzleUnrlsd Feb 7, 2026
eb4a72e
refactor: move invalid base reconstruction analysis
SizzleUnrlsd Feb 7, 2026
d3a5ccc
refactor: move stack buffer analysis
SizzleUnrlsd Feb 7, 2026
039739a
refactor: move ir value utilities
SizzleUnrlsd Feb 7, 2026
f153e9c
refactor: move const-parameter analysis
SizzleUnrlsd Feb 7, 2026
2c384b0
refactor: move size-minus-k analysis
SizzleUnrlsd Feb 7, 2026
02a3c96
refactor: move integer range analysis
SizzleUnrlsd Feb 7, 2026
06422c5
refactor: move stack computation
SizzleUnrlsd Feb 7, 2026
97ab66c
refactor: move input pipeline logic
SizzleUnrlsd Feb 7, 2026
b9a0f01
refactor: move dynamic alloca analysis
SizzleUnrlsd Feb 7, 2026
88aebf3
refactor: move mem intrinsic overflow analysis
SizzleUnrlsd Feb 7, 2026
a89cb44
refactor: move analyzer utilities
SizzleUnrlsd Feb 7, 2026
31c58b2
refactor: move module passes implementation
SizzleUnrlsd Feb 7, 2026
efeecb8
refactor: move report serialization
SizzleUnrlsd Feb 7, 2026
b4cd897
test: add interproc escape fixture stub
SizzleUnrlsd Feb 7, 2026
76bd325
test: add wrapper size-minus-one case
SizzleUnrlsd Feb 7, 2026
930fb80
test: add strncpy size-minus-one case
SizzleUnrlsd Feb 7, 2026
e0d1934
test: add size-minus-one ir fixture
SizzleUnrlsd Feb 7, 2026
4c2a7a1
test: add unreachable detached region case
SizzleUnrlsd Feb 7, 2026
5d73ffa
test: add mixed-predecessor unreachable case
SizzleUnrlsd Feb 7, 2026
8e15647
test: add unreachable validation case
SizzleUnrlsd Feb 7, 2026
25b3bf8
chore(ci): add scripts
SizzleUnrlsd Feb 7, 2026
62688c9
test(ci): add scripts
SizzleUnrlsd Feb 7, 2026
c500435
chore(precommit): add pre-commit config for conventional commit messages
SizzleUnrlsd Feb 7, 2026
65f64ee
test(precommit): add script
SizzleUnrlsd Feb 7, 2026
62e614e
test: disable test
SizzleUnrlsd Feb 7, 2026
fd419bf
chore: add .gitignore
SizzleUnrlsd Feb 7, 2026
1bc3fa6
test(ci): add workflow
SizzleUnrlsd Feb 7, 2026
253b39a
chore(style): format code with clang-format
SizzleUnrlsd Feb 7, 2026
cfa265b
test: normalize fortified libc names in output
SizzleUnrlsd Feb 7, 2026
d57a11b
fix: normalize size-minus-k sink names across platforms
SizzleUnrlsd Feb 7, 2026
0819c14
fix: use StringRef starts_with/ends_with
SizzleUnrlsd Feb 7, 2026
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
16 changes: 16 additions & 0 deletions .github/workflows/commit-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Commit conventions

on:
push:

jobs:
commit_check:
name: Check conventional commits
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate commit messages
run: |
python3 scripts/ci/commit_checker.py
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

# Build directories
/build/
9 changes: 9 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
repos:
- repo: local
hooks:
- id: conventional-commit-msg
name: conventional commit message
entry: python3 scripts/git/commit_msg_checker.py
language: system
stages: [commit-msg]
pass_filenames: true
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,23 @@ option(BUILD_SHARED_LIB "Build shared library variant" ON)
# ===========================
set(STACK_ANALYZER_SOURCES
src/StackUsageAnalyzer.cpp
src/analysis/AllocaUsage.cpp
src/analysis/AnalyzerUtils.cpp
src/analysis/ConstParamAnalysis.cpp
src/analysis/DynamicAlloca.cpp
src/analysis/FunctionFilter.cpp
src/analysis/IRValueUtils.cpp
src/analysis/IntRanges.cpp
src/analysis/InputPipeline.cpp
src/analysis/InvalidBaseReconstruction.cpp
src/analysis/MemIntrinsicOverflow.cpp
src/analysis/SizeMinusKWrites.cpp
src/analysis/StackBufferAnalysis.cpp
src/analysis/StackComputation.cpp
src/analysis/StackPointerEscape.cpp
src/report/ReportSerialization.cpp
src/mangle.cpp
src/passes/ModulePasses.cpp
)

include_directories(${LLVM_INCLUDE_DIRS})
Expand Down
8 changes: 5 additions & 3 deletions include/StackUsageAnalyzer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,13 @@ namespace ctrace::stack
AllocaTooLarge = 8,
AllocaUsageWarning = 9,
InvalidBaseReconstruction = 10,
ConstParameterNotModified = 11
ConstParameterNotModified = 11,
SizeMinusOneWrite = 12
};

template <> struct EnumTraits<DescriptiveErrorCode>
{
static constexpr std::array<std::string_view, 12> names = {"None",
static constexpr std::array<std::string_view, 13> names = {"None",
"StackBufferOverflow",
"NegativeStackIndex",
"VLAUsage",
Expand All @@ -122,7 +123,8 @@ namespace ctrace::stack
"AllocaTooLarge",
"AllocaUsageWarning",
"InvalidBaseReconstruction",
"ConstParameterNotModified"};
"ConstParameterNotModified",
"SizeMinusOneWrite"};
};

/*
Expand Down
41 changes: 41 additions & 0 deletions include/analysis/AllocaUsage.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <functional>
#include <set>
#include <string>
#include <vector>

#include "StackUsageAnalyzer.hpp"

namespace llvm
{
class AllocaInst;
class DataLayout;
class Function;
class Module;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct AllocaUsageIssue
{
std::string funcName;
std::string varName;
const llvm::AllocaInst* allocaInst = nullptr;

bool userControlled = false; // size derived from argument / non-local value
bool sizeIsConst = false; // size known exactly
bool hasUpperBound = false; // bounded size (from ICmp-derived range)
bool isRecursive = false; // function participates in a recursion cycle
bool isInfiniteRecursive = false; // unconditional self recursion

StackSize sizeBytes = 0; // exact size in bytes (if sizeIsConst)
StackSize upperBoundBytes = 0; // upper bound in bytes (if hasUpperBound)
};

std::vector<AllocaUsageIssue>
analyzeAllocaUsage(llvm::Module& mod, const llvm::DataLayout& DL,
const std::set<const llvm::Function*>& recursiveFuncs,
const std::set<const llvm::Function*>& infiniteRecursionFuncs,
const std::function<bool(const llvm::Function&)>& shouldAnalyze);
} // namespace ctrace::stack::analysis
27 changes: 27 additions & 0 deletions include/analysis/AnalyzerUtils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <string>

#include "StackUsageAnalyzer.hpp"
#include "analysis/StackComputation.hpp"

namespace llvm
{
class Function;
} // namespace llvm

namespace ctrace::stack::analysis
{
std::string formatFunctionNameForMessage(const std::string& name);

std::string getFunctionSourcePath(const llvm::Function& F);

bool getFunctionSourceLocation(const llvm::Function& F, unsigned& line, unsigned& column);

std::string buildMaxStackCallPath(const llvm::Function* F, const CallGraph& CG,
const InternalAnalysisState& state);

bool shouldIncludePath(const std::string& path, const AnalysisConfig& config);

bool functionNameMatches(const llvm::Function& F, const AnalysisConfig& config);
} // namespace ctrace::stack::analysis
32 changes: 32 additions & 0 deletions include/analysis/ConstParamAnalysis.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <functional>
#include <string>
#include <vector>

namespace llvm
{
class Function;
class Module;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct ConstParamIssue
{
std::string funcName;
std::string paramName;
std::string currentType;
std::string suggestedType;
std::string suggestedTypeAlt;
bool pointerConstOnly = false; // ex: T * const param
bool isReference = false;
bool isRvalueRef = false;
unsigned line = 0;
unsigned column = 0;
};

std::vector<ConstParamIssue>
analyzeConstParams(llvm::Module& mod,
const std::function<bool(const llvm::Function&)>& shouldAnalyze);
} // namespace ctrace::stack::analysis
27 changes: 27 additions & 0 deletions include/analysis/DynamicAlloca.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <functional>
#include <string>
#include <vector>

namespace llvm
{
class AllocaInst;
class Function;
class Module;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct DynamicAllocaIssue
{
std::string funcName;
std::string varName;
std::string typeName;
const llvm::AllocaInst* allocaInst = nullptr;
};

std::vector<DynamicAllocaIssue>
analyzeDynamicAllocas(llvm::Module& mod,
const std::function<bool(const llvm::Function&)>& shouldAnalyze);
} // namespace ctrace::stack::analysis
27 changes: 27 additions & 0 deletions include/analysis/FunctionFilter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <string>

#include "StackUsageAnalyzer.hpp"

namespace llvm
{
class Function;
class Module;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct FunctionFilter
{
bool hasPathFilter = false;
bool hasFuncFilter = false;
bool hasFilter = false;
std::string moduleSourcePath;
const AnalysisConfig* config = nullptr;

bool shouldAnalyze(const llvm::Function& F) const;
};

FunctionFilter buildFunctionFilter(const llvm::Module& mod, const AnalysisConfig& config);
} // namespace ctrace::stack::analysis
18 changes: 18 additions & 0 deletions include/analysis/IRValueUtils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <string>

namespace llvm
{
class AllocaInst;
class ConstantInt;
class Function;
class Value;
} // namespace llvm

namespace ctrace::stack::analysis
{
std::string deriveAllocaName(const llvm::AllocaInst* AI);

const llvm::ConstantInt* tryGetConstFromValue(const llvm::Value* V, const llvm::Function& F);
} // namespace ctrace::stack::analysis
31 changes: 31 additions & 0 deletions include/analysis/InputPipeline.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <memory>
#include <string>

#include "StackUsageAnalyzer.hpp"

namespace llvm
{
class LLVMContext;
class Module;
class SMDiagnostic;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct ModuleLoadResult
{
std::unique_ptr<llvm::Module> module;
LanguageType language = LanguageType::Unknown;
std::string error;
};

LanguageType detectFromExtension(const std::string& path);

LanguageType detectLanguageFromFile(const std::string& path, llvm::LLVMContext& ctx);

ModuleLoadResult loadModuleForAnalysis(const std::string& filename,
const AnalysisConfig& config, llvm::LLVMContext& ctx,
llvm::SMDiagnostic& err);
} // namespace ctrace::stack::analysis
22 changes: 22 additions & 0 deletions include/analysis/IntRanges.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <map>

namespace llvm
{
class Function;
class Value;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct IntRange
{
bool hasLower = false;
long long lower = 0;
bool hasUpper = false;
long long upper = 0;
};

std::map<const llvm::Value*, IntRange> computeIntRangesFromICmps(llvm::Function& F);
} // namespace ctrace::stack::analysis
33 changes: 33 additions & 0 deletions include/analysis/InvalidBaseReconstruction.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <functional>
#include <string>
#include <vector>

#include "StackUsageAnalyzer.hpp"

namespace llvm
{
class DataLayout;
class Function;
class Instruction;
class Module;
} // namespace llvm

namespace ctrace::stack::analysis
{
struct InvalidBaseReconstructionIssue
{
std::string funcName;
std::string varName; // nom de la variable alloca (stack object)
std::string sourceMember; // membre source (ex: "b")
int64_t offsetUsed = 0; // offset utilisé dans le calcul (peut être négatif)
std::string targetType; // type vers lequel on cast (ex: "struct A*")
bool isOutOfBounds = false; // true si on peut prouver que c'est hors bornes
const llvm::Instruction* inst = nullptr;
};

std::vector<InvalidBaseReconstructionIssue> analyzeInvalidBaseReconstructions(
llvm::Module& mod, const llvm::DataLayout& DL,
const std::function<bool(const llvm::Function&)>& shouldAnalyze);
} // namespace ctrace::stack::analysis
Loading
Loading