Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ permissions:
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# pull_request:
# branches: [ master ]

# cancel running workflows when new commits are being pushed in pull requests
# but not on the master branch
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: CIFuzz
on: [pull_request]
on: []
jobs:
Fuzzing:
runs-on: ubuntu-22.04
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/microwalk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build & Analyze with Microwalk

on:
push:
pull_request:
workflow_dispatch:

env:
script_directory: src/microwalk

jobs:
build-analyze:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3

- name: Setup Build Agent
uses: ./.github/actions/setup-build-agent
with:
target: shared
cache-key: linux-gcc-x86_64-microwalk

- name: Build Botan
run: python3 ./src/scripts/ci_build.py --cc='gcc' microwalk

- name: Run Microwalk analysis
id: run_microwalk
uses: microwalk-project/microwalk-pin-action@v1
with:
script-directory: ${{ env.script_directory }}

- name: Upload analysis result
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ github.workspace }}/${{ env.script_directory }}/results/report.sarif
checkout_path: ${{ github.workspace }}

- name: Archive analysis artifacts
uses: actions/upload-artifact@v3
with:
name: leakage-analysis-results
path: ${{ github.workspace }}/${{ env.script_directory }}/results
41 changes: 41 additions & 0 deletions src/microwalk/analyze.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

set -e

thisDir=$(pwd)
repoRootDir=$(realpath $thisDir/../..)
resultsDir=$thisDir/results

mkdir -p $resultsDir

reports=""

for target in $(find . -name "target-*.cpp" -print)
do
targetName=$(basename -- ${target%.*})

echo "Running target ${targetName}..."

export TESTCASE_DIRECTORY=$thisDir/testcases/$targetName
export TARGET_NAME=$targetName

mkdir -p $WORK_DIR/$targetName/work
mkdir -p $WORK_DIR/$targetName/persist

cd $MICROWALK_PATH
time dotnet Microwalk.dll $thisDir/config.yml

cd $CQR_GENERATOR_PATH
reportFile=$resultsDir/report-$targetName.sarif
dotnet CiReportGenerator.dll $WORK_DIR/$targetName/persist/results/call-stacks.json $targetName $reportFile sarif dwarf $thisDir $repoRootDir

cd $thisDir
cp $WORK_DIR/$targetName/persist/results/call-stacks.txt $resultsDir/call-stacks-$targetName.txt

reports="${reports} ${reportFile}"

echo "Running target ${targetName} successful, generated report ${reportFile}"
done

echo "Merging report files..."
cat $reports | jq -s '.[0].runs[0].results=([.[].runs[0].results]|flatten)|.[0]' > $resultsDir/report.sarif
29 changes: 29 additions & 0 deletions src/microwalk/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

thisDir=$(pwd)
mainDir=$(realpath $thisDir/../..)

# Build library
pushd $mainDir
# Note: we already build botan before the Microwalk action
dwarfdump -l libbotan-3.so >$thisDir/libbotan-3.so.dwarf
popd

# Generate MAP file for library
pushd $MAP_GENERATOR_PATH
dotnet MapFileGenerator.dll $mainDir/libbotan-3.so $thisDir/libbotan-3.map
popd

# Build targets
for target in $(find . -name "target-*.cpp" -print)
do
targetName=$(basename -- ${target%.*})

g++ main.cpp $targetName.cpp -g -fno-inline -fno-split-stack -L "$mainDir" -lbotan-3 -I "$mainDir/build/include" -std=c++20 -o $targetName

pushd $MAP_GENERATOR_PATH
dotnet MapFileGenerator.dll $thisDir/$targetName $thisDir/$targetName.map
popd

dwarfdump -l $targetName >$targetName.dwarf
done
58 changes: 58 additions & 0 deletions src/microwalk/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
constants:
TARGET_PATH: $$CONFIG_PATH$$/$$$TARGET_NAME$$$
LIBRARY_PATH: $$CONFIG_PATH$$/../../
WORK_DIR: $$$WORK_DIR$$$/$$$TARGET_NAME$$$
---

general:
logger:
log-level: debug
file: $$WORK_DIR$$/work/log.txt
monitor:
enable: true
sample-rate: 50

testcase:
module: load
module-options:
input-directory: $$$TESTCASE_DIRECTORY$$$

trace:
module: pin
module-options:
output-directory: $$WORK_DIR$$/work/traces
pin-tool-path: $$$PINTOOL$$$
pin-path: $$$PIN_PATH$$$/pin
wrapper-path: $$TARGET_PATH$$
environment:
LD_LIBRARY_PATH: $$LIBRARY_PATH$$
images:
- $$$TARGET_NAME$$$
- libbotan-3.so
options:
input-buffer-size: 4

preprocess:
module: pin
module-options:
output-directory: $$WORK_DIR$$/work/traces
store-traces: true
keep-raw-traces: false
options:
input-buffer-size: 2
max-parallel-threads: 4

analysis:
modules:
- module: control-flow-leakage
module-options:
output-directory: $$WORK_DIR$$/persist/results
map-files:
- $$TARGET_PATH$$.map
- libbotan-3.map
dump-call-tree: false
include-testcases-in-call-stacks: false

options:
input-buffer-size: 1
max-parallel-threads: 1
131 changes: 131 additions & 0 deletions src/microwalk/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "main.h"

#include <cstdint>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <fstream>
#include <string>

#include <sys/resource.h>

// Pin notification functions.
// These functions (and their names) must not be optimized away by the compiler, so Pin can find and instrument them.
// The return values reduce the probability that the compiler uses these function in other places as no-ops (Visual C++ did do this in some experiments).
#pragma optimize("", off)
extern "C" int PinNotifyTestcaseStart(int t) { return t + 42; }
extern "C" int PinNotifyTestcaseEnd() { return 42; }
extern "C" int PinNotifyStackPointer(uint64_t spMin, uint64_t spMax) { return static_cast<int>(spMin + spMax + 42); }
extern "C" int PinNotifyAllocation(uint64_t address, uint64_t size) { return (int)(address + 23 * size); }
#pragma optimize("", on)

void Microwalk_Test::critical_section_start(int testcaseId)
{
PinNotifyTestcaseStart(testcaseId);
}

void Microwalk_Test::critical_section_end()
{
PinNotifyTestcaseEnd();
}

// Reads the stack pointer base value and transmits it to Pin.
void ReadAndSendStackPointer()
{
// There does not seem to be a reliable way to get the stack size, so we use an estimation
// Compiling with -fno-split-stack may be desired, to avoid surprises during analysis

// Take the current stack pointer as base value
uintptr_t stackBase;
asm("mov %%rsp, %0" : "=r"(stackBase));

// Get full stack size
struct rlimit stackLimit;
if(getrlimit(RLIMIT_STACK, &stackLimit) != 0)
{
char errBuffer[128];
strerror_r(errno, errBuffer, sizeof(errBuffer));
fprintf(stderr, "Error reading stack limit: [%d] %s\n", errno, errBuffer);
}

uint64_t stackMin = reinterpret_cast<uint64_t>(stackBase) - reinterpret_cast<uint64_t>(stackLimit.rlim_cur);
uint64_t stackMax = (reinterpret_cast<uint64_t>(stackBase) + 0x10000) & ~0xFFFFull; // Round to next higher multiple of 64 kB (should be safe on x86 systems)
PinNotifyStackPointer(stackMin, stackMax);
}

static std::string read_testdata(const std::string& filename)
{
std::vector<std::string> lines;
std::ifstream infile(filename);
if(infile.good() == false)
{
throw std::runtime_error("Error reading test data from '" + filename + "'");
}
std::string line;
while(std::getline(infile, line))
{
if(!line.empty() && line.at(0) != '#')
{
lines.push_back(line);
}
}
if(lines.size() != 1)
{
throw std::runtime_error("Error reading test data from '" + filename + "'. Expected exactly one line.");
}
return lines.at(0);
}

// Main trace target function. The following actions are performed:
// The current action is read from stdin.
// A line with "t" followed by a numeric ID, and another line with a file path determining a new testcase, that is subsequently loaded and fed into the target function, while calling PinNotifyNextFile() beforehand.
// A line with "e 0" terminates the program.
void TraceFunc()
{
// First transmit stack pointer information
ReadAndSendStackPointer();

PinNotifyAllocation((uint64_t)&errno, 8);

// Initialize target library
// InitTarget();
std::unique_ptr<Microwalk_Test> test = creat_test();

// Run until exit is requested
char inputBuffer[512];
char errBuffer[128];
while(true)
{
// Read command and testcase ID (0 for exit command)
char command;
int testcaseId;
fgets(inputBuffer, sizeof(inputBuffer), stdin);
sscanf(inputBuffer, "%c %d", &command, &testcaseId);

// Exit or process given testcase
if(command == 'e')
break;
if(command == 't')
{
// Read testcase file name
fgets(inputBuffer, sizeof(inputBuffer), stdin);
int inputFileNameLength = strlen(inputBuffer);
if(inputFileNameLength > 0 && inputBuffer[inputFileNameLength - 1] == '\n')
inputBuffer[inputFileNameLength - 1] = '\0';

// Load testcase file and run target function
std::string raw_input = read_testdata(inputBuffer);
std::vector<uint8_t> input = test->prepare_input(raw_input);

test->critical_function(testcaseId, input);
}
}
}

// Wrapper entry point.
int main(int argc, const char** argv)
{
// Run target function
TraceFunc();
return 0;
}
44 changes: 44 additions & 0 deletions src/microwalk/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <botan/hex.h>
#include <botan/system_rng.h>

#include <string>
#include <vector>
#include <memory>

class Microwalk_Test
{
public:
Microwalk_Test()
{
m_rng = std::make_shared<Botan::System_RNG>();
}

virtual ~Microwalk_Test() = default;

Microwalk_Test(const Microwalk_Test& other) = delete;
Microwalk_Test(Microwalk_Test&& other) = delete;
Microwalk_Test& operator=(const Microwalk_Test& other) = delete;
Microwalk_Test& operator=(Microwalk_Test&& other) = delete;

virtual std::vector<uint8_t> prepare_input(const std::string& input)
{
return Botan::hex_decode(input);
}

virtual void critical_function(int testcaseId, const std::vector<uint8_t>& input) = 0;

protected:
Botan::RandomNumberGenerator& timing_test_rng()
{
return (*m_rng);
}

static void critical_section_start(int testcaseId);

static void critical_section_end();

private:
std::shared_ptr<Botan::RandomNumberGenerator> m_rng;
};

std::unique_ptr<Microwalk_Test> creat_test();
Loading