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
14 changes: 12 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ jobs:
uses: actions/checkout@v4
with:
path: ASPIS

submodules: recursive
fetch-depth: 0

- name: Install system dependencies
run: |
sudo apt update
Expand Down Expand Up @@ -46,7 +48,7 @@ jobs:
ccache --zero-stats
ccache --show-config
ccache --show-stats

mkdir build
cmake -B build -DLLVM_DIR=${{ github.workspace }}/llvm-build/lib/cmake/llvm/
cmake --build build
Expand Down Expand Up @@ -78,3 +80,11 @@ jobs:
run: |
pytest test.py --no-header -v

- name: Upload comparison counter artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: comparison-counter
path: ${{ github.workspace }}/ASPIS/testing/build/comparison_counter.csv
if-no-files-found: warn

5 changes: 5 additions & 0 deletions aspis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ parse_commands() {

--multiple-errbb Enables multiple error basic blocks in EDDI

--coarse-grained Enables coarse-grained duplication in EDDI

EOF
exit 0
;;
Expand Down Expand Up @@ -242,6 +244,9 @@ EOF
--multiple-errbb)
eddi_options="$eddi_options -multiple-errbb=true";
;;
--coarse-grained)
eddi_options="$eddi_options -coarse-grained=true";
;;
-g)
debug_enabled=true;
clang_options="$clang_options $opt";
Expand Down
7 changes: 6 additions & 1 deletion passes/ASPIS.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "Utils/Utils.h"
#include <map>
#include <set>
#include <unordered_set>

using namespace llvm;

Expand Down Expand Up @@ -41,13 +42,16 @@ class EDDI : public PassInfoMixin<EDDI> {
std::set<Value*> toHardenVariables;
std::set<Value*> DuplicatedCalls;
std::unordered_multimap<Value *, Value *> DuplicatedInstructionMap;
std::unordered_set<Instruction *> ClonedInstructions;

std::string entryPoint;

LinkageMap linkageMap;

bool duplicateAll;
bool MultipleErrBBEnabled;
bool CoarseGrainedDuplicationEnabled;
int comparisonCounter = 0;

void preprocess(Module &Md);
void fixDuplicatedConstructors(Module &Md);
Expand All @@ -72,8 +76,9 @@ class EDDI : public PassInfoMixin<EDDI> {
Value *getDuplicateValue(Value *V, Instruction *I);

void fixGlobalCtors(Module &M);
void repairBasicBlock(BasicBlock &BB);
public:
explicit EDDI(bool duplicateAll, bool MultipleErrBBEnabled = false, std::string entryPoint = "main") : duplicateAll(duplicateAll), MultipleErrBBEnabled(MultipleErrBBEnabled), entryPoint(entryPoint) {}
explicit EDDI(bool duplicateAll, bool MultipleErrBBEnabled = false, bool CoarseGrainedDuplicationEnabled = true, std::string entryPoint = "main") : duplicateAll(duplicateAll), MultipleErrBBEnabled(MultipleErrBBEnabled), CoarseGrainedDuplicationEnabled(CoarseGrainedDuplicationEnabled), entryPoint(entryPoint) {}

PreservedAnalyses run(Module &M,
ModuleAnalysisManager &);
Expand Down
91 changes: 89 additions & 2 deletions passes/EDDI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ EDDI::cloneInstr(Instruction &I) {
} // else place it right after the instruction we are working on
else {
IClone->insertAfter(&I);
ClonedInstructions.insert(IClone);
}
DuplicatedInstructionMap.insert(
std::pair<Instruction *, Instruction *>(&I, IClone));
Expand Down Expand Up @@ -761,8 +762,10 @@ Value *EDDI::comparePtrs(Value &V1, Value &V2, IRBuilder<> &B) {
Instruction *L1 = B.CreateLoad(F1->getType(), F1);
Instruction *L2 = B.CreateLoad(F2->getType(), F2);
if (L1->getType()->isFloatingPointTy()) {
comparisonCounter++;
return B.CreateCmp(CmpInst::FCMP_UEQ, L1, L2);
} else {
comparisonCounter++;
return B.CreateCmp(CmpInst::ICMP_EQ, L1, L2);
}
}
Expand Down Expand Up @@ -801,6 +804,7 @@ void EDDI::addConsistencyChecks(
if (Original->getType()->isIntOrIntVectorTy() || Original->getType()->isPtrOrPtrVectorTy()) {
// DuplicatedInstructionMap.insert(std::pair<Value *, Value *>(&I, &I));
CmpInstructions.push_back(B.CreateCmp(CmpInst::ICMP_EQ, Original, Copy));
comparisonCounter++;
}
}
}
Expand Down Expand Up @@ -854,9 +858,11 @@ void EDDI::addConsistencyChecks(
if (OriginalElem->getType()->isFloatingPointTy()) {
CmpInstructions.push_back(
B.CreateCmp(CmpInst::FCMP_UEQ, OriginalElem, CopyElem));
comparisonCounter++;
} else if (OriginalElem->getType()->isIntOrIntVectorTy() || OriginalElem->getType()->isPtrOrPtrVectorTy()) {
CmpInstructions.push_back(
B.CreateCmp(CmpInst::ICMP_EQ, OriginalElem, CopyElem));
comparisonCounter++;
} else {
errs() << "Warning: Didn't create a comparison for ";
OriginalElem->getType()->print(errs());
Expand All @@ -871,9 +877,11 @@ void EDDI::addConsistencyChecks(
if (Original->getType()->isFloatingPointTy()) {
CmpInstructions.push_back(
B.CreateCmp(CmpInst::FCMP_UEQ, Original, Copy));
comparisonCounter++;
} else if (Original->getType()->isIntOrIntVectorTy() || Original->getType()->isPtrOrPtrVectorTy()) {
CmpInstructions.push_back(
B.CreateCmp(CmpInst::ICMP_EQ, Original, Copy));
comparisonCounter++;
} else {
errs() << "Warning: Didn't create a comparison for " << Original->getType() << " type\n";
}
Expand Down Expand Up @@ -1584,6 +1592,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) {

// list of duplicated instructions to remove since they are equal to the original
std::set<CallBase *> GrayAreaCallsToFix;
ClonedInstructions.clear();
int iFn = 1;
LLVM_DEBUG(dbgs() << "Iterating over the functions...\n");

Expand Down Expand Up @@ -1651,6 +1660,15 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) {
}
}
}

if(CoarseGrainedDuplicationEnabled) {
LLVM_DEBUG(dbgs() << "Applying coarse grained duplication\n");
// Apply coarse grained duplication
for (BasicBlock &BB : *Fn) {
repairBasicBlock(BB);
}
}

LLVM_DEBUG(dbgs() << " [done]\n");

// insert the code for calling the error basic block in case of a mismatch
Expand Down Expand Up @@ -1849,6 +1867,8 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) {
LLVM_DEBUG(dbgs() << "Persisting Compiled Functions...\n");
persistCompiledFunctions(CompiledFuncs, "compiled_eddi_functions.csv");

std::cout << "Comparison Counter: " << comparisonCounter << "\n";

return PreservedAnalyses::none();
}

Expand Down Expand Up @@ -2074,6 +2094,68 @@ void EDDI::fixGlobalCtors(Module &M) {
NewGlobalCtors->setSection(Section);
}

/**
* REPAIR groups all original instructions first, followed by all their
* duplicates, producing the coarse-grain order:
* A, B, C, A_dup, B_dup, C_dup
*
* This layout increases the temporal and spatial distance between a MI/SI
* pair. As a result, a single transient fault (SEU) is less likely to
* corrupt both the original and its duplicate before a consistency check
* can detect the mismatch (improving error-detection coverage for
* spatially-correlated faults)
*
* The function is called after the EDDI duplication phase has already
* cloned every eligible instruction and wired up operands. It only moves
* instructions;
*
* @param BB The basic block whose instructions are to be
* reordered
*/
void EDDI::repairBasicBlock(BasicBlock &BB) {

// Collect all duplicated instructions in this BB,
// preserving their relative order so that data-flow dependencies
// among duplicates remain satisfied after the move
std::vector<Instruction *> DupsInOrder;

Instruction *InsertionPoint = BB.getFirstNonPHI();
for (Instruction &I : BB) {
// PHINodes must stay at the top of the block (LLVM invariant).
// AllocaInsts are kept in the entry block's alloca region.
// Terminators (br, ret, switch, …) must remain last.
// None of these should be relocated

if (ClonedInstructions.find(&I) != ClonedInstructions.end()) {
// If this instruction belongs to the cloned set, it is a duplicate
// that needs to be sunk to the bottom of the block
DupsInOrder.push_back(&I);
} else if (I.isTerminator() || isa<CallBase>(I)) {
// If there are no duplicates in this block, nothing to reorder
if (DupsInOrder.empty()) {
continue;
}

// Move every duplicate just before the terminator, in their
// original relative order
for (Instruction *Dup : DupsInOrder) {
if(isa<PHINode>(Dup)){
Dup->moveBefore(InsertionPoint);
}
else{
Dup->moveBefore(&I);
}
}
DupsInOrder.clear();
InsertionPoint = I.getNextNode();
if(InsertionPoint == nullptr) {
return;
}
}
}
}


//-----------------------------------------------------------------------------
// New PM Registration
//-----------------------------------------------------------------------------
Expand All @@ -2082,6 +2164,11 @@ static llvm::cl::opt<bool> MultipleErrBB(
llvm::cl::desc("Enable multiple error basic blocks in EDDI"),
llvm::cl::init(false));

static llvm::cl::opt<bool> CoarseGrained(
"coarse-grained",
llvm::cl::desc("Enable coarse-grained duplication in EDDI"),
llvm::cl::init(false));

llvm::PassPluginLibraryInfo getEDDIPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "eddi-verify", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
Expand All @@ -2099,9 +2186,9 @@ llvm::PassPluginLibraryInfo getEDDIPluginInfo() {
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "eddi-verify") {
#ifdef DUPLICATE_ALL
FPM.addPass(EDDI(true, MultipleErrBB));
FPM.addPass(EDDI(true, MultipleErrBB, CoarseGrained));
#else
FPM.addPass(EDDI(false, MultipleErrBB));
FPM.addPass(EDDI(false, MultipleErrBB, CoarseGrained));
#endif
return true;
}
Expand Down
27 changes: 25 additions & 2 deletions testing/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import pytest_timeout
import tomllib
import os
import re

# Default configurations
ASPIS_SCRIPT = "../aspis.sh" # Path to the ASPIS compilation script
Expand All @@ -12,10 +14,21 @@
DOCKER_SHARED_VOLUME = "/workspace/ASPIS/tmp"
LOCAL_SHARED_VOLUME = "./tests/"
DOCKER_COMPOSE_FILE = "../docker/docker-compose.yml"
COMPARISON_COUNTER_PATH = "./build/comparison_counter.csv"

data_techniques = ["--no-dup", "--eddi", "--eddi --multiple-errbb", "--reddi", "--reddi --multiple-errbb", "--seddi", "--fdsc"]
data_techniques = ["--no-dup", "--eddi", "--eddi --multiple-errbb", "--eddi --coarse-grained", "--eddi --multiple-errbb --coarse-grained", "--reddi", "--reddi --multiple-errbb", "--reddi --coarse-grained", "--reddi --multiple-errbb --coarse-grained", "--seddi", "--fdsc"]
cfc_techniques = ["--no-cfc", "--cfcss", "--rasm", "--racfed", "--inter-rasm"]

def record_comparison_counter(test_name: str, output: str):
comparison_file = open(COMPARISON_COUNTER_PATH, "a+")
m = re.search(r"Comparison Counter:\s*(\d+)", output)
if m:
counter = m.group(1)
else:
counter = "NA"
comparison_file.write(f"{test_name},{counter}\n")
comparison_file.close()

# Load the test configuration
def load_config():
assert os.path.exists("../aspis.sh") and "Cannot find aspis.sh, please run the tests from the ASPIS testing directory as `pytest test.py`"
Expand Down Expand Up @@ -61,6 +74,15 @@ def execute_binary(local_build_dir, test_name):
def pytest_generate_tests(metafunc):
"""Custom hook to parametrize tests based on the CLI --tests-file flag."""
if "test_data" in metafunc.fixturenames:
os.makedirs("./build", exist_ok=True)

if os.path.exists(COMPARISON_COUNTER_PATH):
os.remove(COMPARISON_COUNTER_PATH)

comparison_file = open(COMPARISON_COUNTER_PATH, "a+")
comparison_file.write("test_name,comparison_counter\n")
comparison_file.close()

tests_file_paths = metafunc.config.getoption("--tests-file")
test_list = []
for file_path in tests_file_paths:
Expand Down Expand Up @@ -121,7 +143,8 @@ def test_aspis(test_data, use_container, aspis_addopt, data_technique, cfc_techn
test_name_complete = f"{test_name}_{data_technique}_{cfc_technique}".replace("--", "").replace(" ", "_").replace("=", "")

# Compile the source file
compile_with_aspis(source_path, test_name_complete, aspis_options, llvm_bin, docker_build_dir)
compilation_result = compile_with_aspis(source_path, test_name_complete, aspis_options, llvm_bin, docker_build_dir)
record_comparison_counter(test_name_complete, compilation_result)

# Execute the binary and check output
result = execute_binary(local_build_dir, test_name_complete)
Expand Down
Loading