Skip to content

Commit

Permalink
FaultInjector: Notify the user when a fault is injected (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
alessandrogario authored Dec 26, 2019
1 parent b6062f4 commit 6506b68
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 11 deletions.
2 changes: 1 addition & 1 deletion libraries/ebpf-common/src
95 changes: 88 additions & 7 deletions src/faultinjector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct FaultInjector::PrivateData final {
: perf_event_array(perf_event_array_) {}

ebpf::PerfEventArray &perf_event_array;
std::uint32_t event_data_size{0U};

ProcessIDFilter filter;
Configuration::SyscallFault config;
Expand Down Expand Up @@ -50,6 +51,10 @@ FaultInjector::create(ebpf::PerfEventArray &perf_event_array,

FaultInjector::~FaultInjector() {}

std::uint64_t FaultInjector::eventIdentifier() const {
return static_cast<std::uint64_t>(d->event_fd.get());
}

FaultInjector::FaultInjector(ebpf::PerfEventArray &perf_event_array,
const Configuration::SyscallFault &config,
const ProcessIDFilter &filter)
Expand Down Expand Up @@ -98,13 +103,29 @@ SuccessOrStringError FaultInjector::generateBPFProgram() {

// Generate the pt_regs structure
std::vector<llvm::Type *> type_list(21U, llvm::Type::getInt64Ty(d->context));

auto pt_regs_struct = llvm::StructType::create(type_list, "pt_regs", true);
auto pt_regs_struct = llvm::StructType::create(type_list, "pt_regs", false);

if (pt_regs_struct == nullptr) {
return StringError::create("Failed to create the pt_regs structure type");
}

// Generate the event data structure (timestamp + event_id + (pid/tgid) +
// injected error
// + pt_regs)
type_list =
std::vector<llvm::Type *>(25U, llvm::Type::getInt64Ty(d->context));

auto event_data_struct =
llvm::StructType::create(type_list, "EventData", true);

if (event_data_struct == nullptr) {
return StringError::create(
"Failed to create the event data structure type");
}

d->event_data_size = static_cast<std::uint32_t>(
ebpf::getLLVMStructureSize(event_data_struct, d->module.get()));

// Create the entry point function
auto function_type =
llvm::FunctionType::get(llvm::Type::getInt64Ty(d->context),
Expand All @@ -121,16 +142,22 @@ SuccessOrStringError FaultInjector::generateBPFProgram() {
auto section_name = d->config.name + "_section";

function->setSection(section_name);
function->arg_begin()->setName("ctx");

auto kprobe_context = function->arg_begin();
kprobe_context->setName("ctx");

auto entry_bb = llvm::BasicBlock::Create(d->context, "entry", function);

// Generate the PID filtering logic
// Allocate space for the event data
llvm::IRBuilder<> builder(d->context);
builder.SetInsertPoint(entry_bb);

auto event_data = builder.CreateAlloca(event_data_struct);

// Generate the PID filtering logic
auto bpf_syscall_interface_exp =
tob::ebpf::BPFSyscallInterface::create(builder);

if (!bpf_syscall_interface_exp.succeeded()) {
return bpf_syscall_interface_exp.error();
}
Expand Down Expand Up @@ -169,8 +196,8 @@ SuccessOrStringError FaultInjector::generateBPFProgram() {
}

builder.SetInsertPoint(fail_syscall_bb);
auto gen_status =
generateFaultSelector(builder, *bpf_syscall_interface.get());
auto gen_status = generateFaultSelector(
builder, *bpf_syscall_interface.get(), event_data, kprobe_context);

if (gen_status.failed()) {
return gen_status.error();
Expand All @@ -187,7 +214,8 @@ SuccessOrStringError FaultInjector::generateBPFProgram() {

SuccessOrStringError FaultInjector::generateFaultSelector(
llvm::IRBuilder<> &builder,
ebpf::BPFSyscallInterface &bpf_syscall_interface) {
ebpf::BPFSyscallInterface &bpf_syscall_interface, llvm::Value *event_data,
llvm::Value *pt_regs) {

struct FaultRange final {
std::uint8_t start{0U};
Expand Down Expand Up @@ -250,6 +278,59 @@ SuccessOrStringError FaultInjector::generateFaultSelector(

builder.SetInsertPoint(fail_syscall_bb);

//
// Populate the event data structure (timestamp + event_id + (pid/tgid) +
// injected error
// + pt_regs)
//

// Timestamp
auto timestamp = bpf_syscall_interface.ktimeGetNs();

auto event_data_field_ptr = builder.CreateGEP(
event_data, {builder.getInt32(0), builder.getInt32(0U)});

builder.CreateStore(timestamp, event_data_field_ptr);

// Event identifier
event_data_field_ptr = builder.CreateGEP(
event_data, {builder.getInt32(0), builder.getInt32(1U)});

builder.CreateStore(builder.getInt64(eventIdentifier()),
event_data_field_ptr);

// Thread id + process id
auto pid_tgid = bpf_syscall_interface.getCurrentPidTgid();

event_data_field_ptr = builder.CreateGEP(
event_data, {builder.getInt32(0), builder.getInt32(2U)});

builder.CreateStore(pid_tgid, event_data_field_ptr);

// Injected error code
event_data_field_ptr = builder.CreateGEP(
event_data, {builder.getInt32(0), builder.getInt32(3U)});

builder.CreateStore(builder.getInt64(fault.exit_code),
event_data_field_ptr);

// Context structure
for (std::uint32_t i = 0U; i < 21U; ++i) {
auto reg_value_ptr = builder.CreateGEP(
pt_regs, {builder.getInt32(0), builder.getInt32(i)});

auto reg_value = builder.CreateLoad(reg_value_ptr);

auto reg_dest_ptr = builder.CreateGEP(
event_data, {builder.getInt32(0), builder.getInt32(4 + i)});

builder.CreateStore(reg_value, reg_dest_ptr);
}

bpf_syscall_interface.perfEventOutput(pt_regs, d->perf_event_array.fd(),
static_cast<std::uint32_t>(-1LL),
event_data, d->event_data_size);

bpf_syscall_interface.overrideReturn(current_function->arg_begin(),
fault.exit_code);

Expand Down
39 changes: 38 additions & 1 deletion src/faultinjector.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,49 @@ class FaultInjector final {
std::vector<int> process_id_list;
};

#pragma pack(push, 1)
struct EventData final {
std::uint64_t timestamp;
std::uint64_t event_id;
std::uint32_t process_id;
std::uint32_t thread_id;
std::uint64_t injected_error;
std::uint64_t r15;
std::uint64_t r14;
std::uint64_t r13;
std::uint64_t r12;
std::uint64_t rbp;
std::uint64_t rbx;
std::uint64_t r11;
std::uint64_t r10;
std::uint64_t r9;
std::uint64_t r8;
std::uint64_t rax;
std::uint64_t rcx;
std::uint64_t rdx;
std::uint64_t rsi;
std::uint64_t rdi;
std::uint64_t orig_rax;
std::uint64_t rip;
std::uint64_t cs;
std::uint64_t eflags;
std::uint64_t rsp;
std::uint64_t ss;
};
#pragma pack(pop)

static_assert(sizeof(EventData) == 25 * sizeof(std::uint64_t),
"EventData is not correctly defined");

using Ref = std::unique_ptr<FaultInjector>;
static StringErrorOr<Ref> create(ebpf::PerfEventArray &perf_event_array,
const Configuration::SyscallFault &config,
const ProcessIDFilter &filter);

~FaultInjector();

std::uint64_t eventIdentifier() const;

FaultInjector(const FaultInjector &) = delete;
FaultInjector &operator=(const FaultInjector &) = delete;

Expand All @@ -52,7 +88,8 @@ class FaultInjector final {

SuccessOrStringError
generateFaultSelector(llvm::IRBuilder<> &builder,
ebpf::BPFSyscallInterface &bpf_syscall_interface);
ebpf::BPFSyscallInterface &bpf_syscall_interface,
llvm::Value *event_data, llvm::Value *pt_regs);

SuccessOrStringError loadBPFProgram();
};
Expand Down
79 changes: 78 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <iomanip>
#include <iostream>
#include <thread>
#include <unordered_map>

#include <fcntl.h>
#include <semaphore.h>
Expand All @@ -22,6 +23,74 @@

#include <tob/ebpf/perfeventarray.h>

#define EBPFAULT_DUMP_REGISTER(register_name) \
do { \
std::cout << std::setfill(' ') << std::setw(10) << #register_name << " " \
<< std::hex << std::setfill('0') << std::setw(16) \
<< event_data.register_name << " "; \
} while (false)

std::unordered_map<std::uint64_t, std::string> event_name_map;

void printEventData(const std::vector<std::uint8_t> &buffer) {
if (buffer.size() != 200U) {
return;
}

tob::ebpfault::FaultInjector::EventData event_data;
std::memcpy(&event_data, buffer.data(), sizeof(event_data));

std::string event_name;
auto event_name_it = event_name_map.find(event_data.event_id);
if (event_name_it == event_name_map.end()) {
event_name = std::to_string(event_data.event_id);
} else {
event_name = event_name_it->second;
}

std::cout << "timestamp: " << std::dec << event_data.timestamp
<< " syscall: " << event_name
<< " process_id: " << event_data.process_id
<< " thread_id: " << event_data.thread_id << " injected_error: "
<< tob::ebpfault::describeFaultValue(event_data.injected_error)
<< "\n";

EBPFAULT_DUMP_REGISTER(r15);
EBPFAULT_DUMP_REGISTER(r14);
EBPFAULT_DUMP_REGISTER(r13);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(r12);
EBPFAULT_DUMP_REGISTER(rbp);
EBPFAULT_DUMP_REGISTER(rbx);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(r11);
EBPFAULT_DUMP_REGISTER(r10);
EBPFAULT_DUMP_REGISTER(r9);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(r8);
EBPFAULT_DUMP_REGISTER(rax);
EBPFAULT_DUMP_REGISTER(rcx);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(rdx);
EBPFAULT_DUMP_REGISTER(rsi);
EBPFAULT_DUMP_REGISTER(rdi);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(orig_rax);
EBPFAULT_DUMP_REGISTER(rip);
EBPFAULT_DUMP_REGISTER(cs);
std::cout << "\n";

EBPFAULT_DUMP_REGISTER(eflags);
EBPFAULT_DUMP_REGISTER(rsp);
EBPFAULT_DUMP_REGISTER(ss);
std::cout << "\n\n";
}

int main(int argc, char *argv[], char *envp[]) {
auto command_line_params_exp = tob::ebpfault::parseCommandLine(argc, argv);
if (!command_line_params_exp.succeeded()) {
Expand Down Expand Up @@ -140,6 +209,9 @@ int main(int argc, char *argv[], char *envp[]) {
}

auto fault_injector = fault_injector_exp.takeValue();

event_name_map.insert({fault_injector->eventIdentifier(), config.name});

fault_injector_list.push_back(std::move(fault_injector));

std::cout << "\n";
Expand All @@ -166,7 +238,12 @@ int main(int argc, char *argv[], char *envp[]) {
break;
}

std::this_thread::sleep_for(std::chrono::seconds(1));
std::vector<std::uint8_t> buffer;
if (!perf_event_array->read(buffer)) {
continue;
}

printEventData(buffer);
}

if (execve_semaphore != nullptr) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ std::string describeFaultValue(std::uint64_t fault_value) {
}

std::stringstream stream;
stream << fault_value << " (0x" << std::hex << fault_value << ")";
stream << fault_value << "(0x" << std::hex << fault_value << ")";

return stream.str();
}
Expand Down

0 comments on commit 6506b68

Please sign in to comment.