Skip to content
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
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
branches: [ main, feature/stack-buffer-bounds-check ]
pull_request:

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build build-essential python3

- name: Configure and build (CMake)
run: |
mkdir -p build
cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release
ninja stack_usage_analyzer

- name: Run analyzer tests (Python framework)
run: |
python3 run_tests.py
99 changes: 80 additions & 19 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
cmake_minimum_required(VERSION 3.16)
project(stack_usage_analyzer)

include(${CMAKE_SOURCE_DIR}/cmake/CheckLLVMVersion.cmake)
set(LLVM_MIN_REQUIRED_VERSION "19" CACHE STRING "Minimum required LLVM version")
check_llvm_version(${LLVM_MIN_REQUIRED_VERSION})
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(CheckLLVMVersion OPTIONAL)

if(COMMAND check_llvm_version)
set(LLVM_MIN_REQUIRED_VERSION "19" CACHE STRING "Minimum required LLVM version")
check_llvm_version(${LLVM_MIN_REQUIRED_VERSION})
else()
message(WARNING "check_llvm_version() not available, skipping LLVM version check")
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # Optionnel mais recommandé
set(CMAKE_CXX_EXTENSIONS OFF)

set(LLVM_LINK_LLVM_DYLIB ON)

Expand All @@ -27,6 +33,18 @@ FetchContent_MakeAvailable(cc)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")

# Options de build
option(BUILD_CLI "Build stack_usage_analyzer CLI tool" ON)
option(BUILD_SHARED_LIB "Build shared library variant" ON)

# ===========================
# Communs Sources
# ===========================
set(STACK_ANALYZER_SOURCES
src/StackUsageAnalyzer.cpp
src/mangle.cpp
)

include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})

Expand All @@ -36,33 +54,76 @@ add_definitions(${LLVM_DEFINITIONS})
# support
# )


# ===== LIBRARY =====
# Contient ta logique d'analyse (pas de main())
add_library(stack_usage_analyzer_lib
src/StackUsageAnalyzer.cpp
add_library(stack_usage_analyzer_lib STATIC
${STACK_ANALYZER_SOURCES}
)

# target_include_directories(stack_usage_analyzer_lib
# PUBLIC
# ${CMAKE_CURRENT_SOURCE_DIR}/include
# ${LLVM_INCLUDE_DIRS}
# )

# FOR USE WITH FETCHCONTENT
target_include_directories(stack_usage_analyzer_lib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${LLVM_INCLUDE_DIRS}
)

# ALIAS FOR USE WITH FETCHCONTENT
add_library(coretrace::stack_usage_analyzer_lib ALIAS stack_usage_analyzer_lib)

# Replace this one :
target_link_libraries(stack_usage_analyzer_lib
PUBLIC
${llvm_libs}
cc::compilerlib_static
)
# by this one :
if(LLVM_LINK_LLVM_DYLIB)
target_link_libraries(stack_usage_analyzer_lib
PUBLIC
LLVM
cc::compilerlib_static
)
else()
llvm_map_components_to_libnames(llvm_libs
core
irreader
support
)
target_link_libraries(stack_usage_analyzer_lib
PUBLIC
${llvm_libs}
cc::compilerlib_static
)
endif()

# # ===== CLI BINARY =====
# add_executable(stack_usage_analyzer
# main.cpp
# )

# ===== CLI BINARY =====
add_executable(stack_usage_analyzer
main.cpp
)
# # target_link_libraries(stack_usage_analyzer PRIVATE ${llvm_libs})
# target_link_libraries(stack_usage_analyzer
# PRIVATE
# stack_usage_analyzer_lib
# cc::compilerlib_static
# )

# target_link_libraries(stack_usage_analyzer PRIVATE ${llvm_libs})
target_link_libraries(stack_usage_analyzer
PRIVATE
stack_usage_analyzer_lib
cc::compilerlib_static
)
# ===== CLI BINARY =====
if(BUILD_CLI)
add_executable(stack_usage_analyzer
main.cpp
)

target_link_libraries(stack_usage_analyzer
PRIVATE
stack_usage_analyzer_lib
# pas besoin de relinker cc::compilerlib_static ici,
# il est déjà dans la lib
)
endif()
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
./stack_usage_analyzer --mode=[abi/ir] test.[ll/c/cpp]
```

```
--quiet coupe complètement les diagnostics
--warnings-only garde seulement les diagnostics importants
````

### Example

Given this code:
Expand Down Expand Up @@ -116,3 +121,33 @@ Function: main
- Define json API
- Unmangling symbols

---

#### 9. Détection de fuite de stack pointer

Exemples :
```c
char buf[10];
return buf; // renvoi pointeur vers stack → use-after-return
```

Ou stockage :

```c
global = buf; // leaking address of stack variable
```

---

Actually done:

- 1. adding VLA : Detection of potentially dangerous dynamic alloca
- 2. Detection of memcpy/memset on stack buffers
- 3. Warning when a function performs multiple stores into the same buffer
- 4. Deeper traversal analysis: constraint propagation
- 5. Detection of deep indirection in aliasing
- 6. Detection of overflow in a struct containing an internal array
- 7. Detection of stack pointer leaks:
- store_unknown -> storing the pointer in a non-local location (typically out-parameter, heap, etc.)
- call_callback -> passing it to a callback (indirect call)
- call_arg -> passing it as an argument to a direct function, potentially capturable
23 changes: 23 additions & 0 deletions extern-project/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.16)
project(consumer_example CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
stack_analyzer
GIT_REPOSITORY https://github.com/CoreTrace/coretrace-stack-analyzer.git
GIT_TAG feature/stack-buffer-bounds-check
# GIT_TAG main
)

FetchContent_MakeAvailable(stack_analyzer)

add_executable(sa_consumer src/main.cpp)

target_link_libraries(sa_consumer
PRIVATE
coretrace::stack_usage_analyzer_lib
)
29 changes: 29 additions & 0 deletions extern-project/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "StackUsageAnalyzer.hpp"
#include <llvm/Support/SourceMgr.h>
#include <llvm/IR/LLVMContext.h>
#include <iostream>

int main(int argc, char **argv)
{
if (argc < 2)
{
std::cerr << "usage: sa_consumer <file.c>\n";
return 1;
}

std::string filename = argv[1];

llvm::LLVMContext ctx;
llvm::SMDiagnostic diag;

ctrace::stack::AnalysisConfig cfg;
cfg.mode = ctrace::stack::AnalysisMode::IR;
cfg.stackLimit = 8 * 1024 * 1024;

auto res = ctrace::stack::analyzeFile(filename, cfg, ctx, diag);

// Exemple : output SARIF sur stdout
std::cout << ctrace::stack::toSarif(res, filename) << std::endl;

return 0;
}
37 changes: 36 additions & 1 deletion include/StackUsageAnalyzer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ enum class AnalysisMode
// Configuration de l'analyse (mode + limite de stack)
struct AnalysisConfig
{
AnalysisMode mode = AnalysisMode::IR;
AnalysisMode mode = AnalysisMode::IR;
StackSize stackLimit = 8ull * 1024ull * 1024ull; // 8 MiB par défaut
bool quiet = false;
bool warningsOnly = false;
};

// Résultat par fonction
Expand All @@ -43,13 +45,46 @@ struct FunctionResult
bool exceedsLimit = false; // maxStack > config.stackLimit
};

enum class DiagnosticSeverity
{
Info = 0,
Warning = 1,
Error = 2
};

struct Diagnostic
{
std::string funcName;
unsigned line = 0;
unsigned column = 0;
DiagnosticSeverity severity = DiagnosticSeverity::Warning;
std::string message;
};

// Résultat global pour un module
struct AnalysisResult
{
AnalysisConfig config;
std::vector<FunctionResult> functions;
// Human-readable diagnostics (buffer overflows, VLAs, memcpy issues, escapes, etc.)
// All messages are formatted and then printed in main().
// std::vector<std::string> diagnostics;
std::vector<Diagnostic> diagnostics;
};

// Serialize an AnalysisResult to a simple JSON format (pour CI / GitHub Actions).
// `inputFile` : chemin du fichier analysé (celui que tu passes à analyzeFile).
std::string toJson(const AnalysisResult &result,
const std::string &inputFile);

// Serialize an AnalysisResult to SARIF 2.1.0 (compatible GitHub Code Scanning).
// `inputFile` : chemin du fichier analysé.
// `toolName` / `toolVersion` : metadata du tool dans le SARIF.
std::string toSarif(const AnalysisResult &result,
const std::string &inputFile,
const std::string &toolName = "coretrace-stack-analyzer",
const std::string &toolVersion = "0.1.0");

// Analyse un module déjà chargé (tu peux réutiliser dans d'autres outils)
AnalysisResult analyzeModule(llvm::Module &mod,
const AnalysisConfig &config);
Expand Down
Loading
Loading