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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Assembly code
*.s

# Prerequisites
*.d

Expand Down
2 changes: 2 additions & 0 deletions IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ add_executable(
phi_pass.c
reachable_bb.c
add_counter_pass.c
add_stack_offset.c
add_constraint_pass.c
)

add_executable(probe probe.c read.c array.c)
Expand Down
16 changes: 12 additions & 4 deletions IR/Readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# TODO
# Verifier

We design a form of constraint that could describe all types of ebpf verifier rules. The verifier will generate a "constaint set" based on static analysis information (e.g. BTF) and that doesn't need any simulation.

Then this constaint set will be passed to our IR and we will add check for those constaints. Since currently our IR is typeless so we can only use some raw constaint generated from the verifier.

## BB users
To start with, a simple constaint would be "range constraint", meaning a register (at a specific position) must be within a range.

Used in PHI nodes.
One opinion, one benefit of designing the raw constraint from is that our runtime-check system will not depend heavily on the current linux verifier and will be portable to other verifiers.

# TODO

## Type checker
- More instructions
- bpf-to-bpf calls
- tail calls
42 changes: 42 additions & 0 deletions IR/add_constraint_pass.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "add_constraint_pass.h"
#include "array.h"
#include "bpf_ir.h"
#include "constraint.h"
#include "dbg.h"
#include "ir_bb.h"
#include "ir_insn.h"

// Initialize some testing constraints
void init_test_constraints(struct ir_function *fun) {
fun->value_constraints = INIT_ARRAY(struct ir_constraint);
}

void add_constraint(struct ir_function *fun) {
init_test_constraints(fun); // For testing purpose

struct ir_basic_block *err_bb = create_bb(fun);
struct ir_value val;
val.type = IR_VALUE_CONSTANT;
val.data.constant_d.type = IR_CONSTANT_U64;
val.data.constant_d.data.u64_d = 1;
create_ret_insn_bb(err_bb, val, INSERT_BACK);

struct ir_constraint *pos;
array_for(pos, fun->value_constraints) {
struct ir_constraint c = *pos;
if (c.type == CONSTRAINT_TYPE_VALUE_EQUAL) {
struct ir_basic_block *newbb = split_bb(fun, c.pos);
create_jbin_insn(c.pos, c.val, c.cval, newbb, err_bb, IR_INSN_JNE, INSERT_FRONT);
connect_bb(c.pos->parent_bb, err_bb);
} else if (c.type == CONSTRAINT_TYPE_VALUE_RANGE) {
struct ir_basic_block *newbb = split_bb(fun, c.pos);
create_jbin_insn(c.pos, c.val, c.start, newbb, err_bb, IR_INSN_JLT, INSERT_FRONT);
connect_bb(c.pos->parent_bb, err_bb);
struct ir_basic_block *newbb2 = split_bb(fun, c.pos);
create_jbin_insn(c.pos, c.val, c.end, newbb2, err_bb, IR_INSN_JGE, INSERT_FRONT);
connect_bb(c.pos->parent_bb, err_bb);
} else {
CRITICAL("Error");
}
}
}
10 changes: 5 additions & 5 deletions IR/add_counter_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ void add_counter(struct ir_function *fun) {
val1.data.constant_d.type = IR_CONSTANT_U64;
val1.data.constant_d.data.u64_d = len;
struct ir_value val2;
val2.type = IR_VALUE_INSN;
val2.data.insn_d = load_insn;
struct ir_insn *added = create_add_insn(load_insn, val1, val2, INSERT_BACK);
val.data.insn_d = added;
val2.type = IR_VALUE_INSN;
val2.data.insn_d = load_insn;
struct ir_insn *added = create_bin_insn(load_insn, val1, val2, IR_INSN_ADD, INSERT_BACK);
val.data.insn_d = added;
struct ir_insn *store_back = create_store_insn(added, alloc_insn, val, INSERT_BACK);
struct ir_basic_block *new_bb = split_bb(fun, store_back);
val2.data.insn_d = added;
val1.data.constant_d.data.u64_d = 0x10000;
create_jlt_insn(store_back, val1, val2, new_bb, err_bb, INSERT_BACK);
create_jbin_insn(store_back, val1, val2, new_bb, err_bb, IR_INSN_JLT, INSERT_BACK);
// Manually connect BBs
connect_bb(bb, err_bb);
}
Expand Down
49 changes: 49 additions & 0 deletions IR/add_stack_offset.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "add_stack_offset.h"
#include "array.h"
#include "bpf_ir.h"
#include "ir_insn.h"

void add_stack_offset(struct ir_function *fun, __s16 offset) {
struct array users = fun->sp_users;
struct ir_insn **pos;
array_for(pos, users) {
struct ir_insn *insn = *pos;

if (insn->op == IR_INSN_LOADRAW || insn->op == IR_INSN_STORERAW) {
insn->addr_val.offset += offset;
}

for (__u8 j = 0; j < insn->value_num; ++j) {
if (insn->values[j].type == IR_VALUE_STACK_PTR) {
// Stack pointer as value
struct ir_value val;
val.type = IR_VALUE_CONSTANT;
val.data.constant_d.type = IR_CONSTANT_S16;
val.data.constant_d.data.s16_d = offset;
struct ir_insn *new_insn =
create_bin_insn(insn, insn->values[j], val, IR_INSN_ADD, INSERT_FRONT);
val.type = IR_VALUE_INSN;
val.data.insn_d = new_insn;
insn->values[j] = val;
}
}
if (insn->op == IR_INSN_PHI) {
struct phi_value *pv_pos2;
array_for(pv_pos2, insn->phi) {
if (pv_pos2->value.type == IR_VALUE_STACK_PTR) {
// Stack pointer as value
struct ir_value val;
val.type = IR_VALUE_CONSTANT;
val.data.constant_d.type = IR_CONSTANT_S16;
val.data.constant_d.data.s16_d = offset;
struct ir_insn *new_insn =
create_bin_insn(insn, pv_pos2->value, val, IR_INSN_ADD, INSERT_FRONT);
val.type = IR_VALUE_INSN;
val.data.insn_d = new_insn;
pv_pos2->value = val;
}
}
}
}
array_free(&fun->sp_users);
}
1 change: 1 addition & 0 deletions IR/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ void array_erase(struct array *arr, size_t idx) {

void array_free(struct array *arr) {
__free(arr->data);
*arr = array_null();
}
91 changes: 56 additions & 35 deletions IR/bpf_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "add_stack_offset.h"
#include "array.h"
#include "ir_insn.h"
#include "list.h"
#include "dbg.h"
#include "passes.h"
Expand Down Expand Up @@ -192,7 +194,8 @@ struct bb_info gen_bb(struct bpf_insn *insns, size_t len) {
new_insn.off = insn.off;
new_insn.pos = pos;
if (pos + 1 < real_bb->end_pos && insns[pos + 1].code == 0) {
new_insn.imm64 = ((__s64)(insns[pos + 1].imm) << 32) | insn.imm;
__u64 imml = (__u64)insn.imm & 0xFFFFFFFF;
new_insn.imm64 = ((__s64)(insns[pos + 1].imm) << 32) | imml;
pos++;
}
real_bb->pre_insns[bb_pos] = new_insn;
Expand Down Expand Up @@ -507,55 +510,71 @@ void transform_bb(struct ssa_transform_env *env, struct pre_ir_basic_block *bb)
// 32-bit ALU class
// TODO: 64-bit ALU class
if (BPF_OP(code) == BPF_ADD) {
struct ir_insn *new_insn = create_insn_back(bb->ir_bb);
new_insn->op = IR_INSN_ADD;
new_insn->values[0] = read_variable(env, insn.dst_reg, bb);
new_insn->values[1] = get_src_value(env, bb, insn);
new_insn->value_num = 2;
add_user(env, new_insn, new_insn->values[0]);
add_user(env, new_insn, new_insn->values[1]);
struct ir_insn *new_insn =
create_bin_insn_bb(bb->ir_bb, read_variable(env, insn.dst_reg, bb),
get_src_value(env, bb, insn), IR_INSN_ADD, INSERT_BACK);

struct ir_value new_val;
new_val.type = IR_VALUE_INSN;
new_val.data.insn_d = new_insn;
write_variable(env, insn.dst_reg, bb, new_val);
} else if (BPF_OP(code) == BPF_SUB) {
struct ir_insn *new_insn = create_insn_back(bb->ir_bb);
new_insn->op = IR_INSN_SUB;
new_insn->values[0] = read_variable(env, insn.dst_reg, bb);
new_insn->values[1] = get_src_value(env, bb, insn);
new_insn->value_num = 2;
add_user(env, new_insn, new_insn->values[0]);
add_user(env, new_insn, new_insn->values[1]);
struct ir_insn *new_insn =
create_bin_insn_bb(bb->ir_bb, read_variable(env, insn.dst_reg, bb),
get_src_value(env, bb, insn), IR_INSN_SUB, INSERT_BACK);

struct ir_value new_val;
new_val.type = IR_VALUE_INSN;
new_val.data.insn_d = new_insn;
write_variable(env, insn.dst_reg, bb, new_val);
} else if (BPF_OP(code) == BPF_MUL) {
struct ir_insn *new_insn = create_insn_back(bb->ir_bb);
new_insn->op = IR_INSN_MUL;
new_insn->values[0] = read_variable(env, insn.dst_reg, bb);
new_insn->values[1] = get_src_value(env, bb, insn);
new_insn->value_num = 2;
add_user(env, new_insn, new_insn->values[0]);
add_user(env, new_insn, new_insn->values[1]);
struct ir_insn *new_insn =
create_bin_insn_bb(bb->ir_bb, read_variable(env, insn.dst_reg, bb),
get_src_value(env, bb, insn), IR_INSN_MUL, INSERT_BACK);
struct ir_value new_val;
new_val.type = IR_VALUE_INSN;
new_val.data.insn_d = new_insn;
write_variable(env, insn.dst_reg, bb, new_val);
} else if (BPF_OP(code) == BPF_MOV) {
// Do not create instructions
write_variable(env, insn.dst_reg, bb, get_src_value(env, bb, insn));
} else {
} else if (BPF_OP(code) == BPF_LSH) {
struct ir_insn *new_insn =
create_bin_insn_bb(bb->ir_bb, read_variable(env, insn.dst_reg, bb),
get_src_value(env, bb, insn), IR_INSN_LSH, INSERT_BACK);
struct ir_value new_val;
new_val.type = IR_VALUE_INSN;
new_val.data.insn_d = new_insn;
write_variable(env, insn.dst_reg, bb, new_val);
} else if (BPF_OP(code) == BPF_MOD) {
// dst = (src != 0) ? (dst % src) : dst
struct ir_insn *new_insn =
create_bin_insn_bb(bb->ir_bb, read_variable(env, insn.dst_reg, bb),
get_src_value(env, bb, insn), IR_INSN_MOD, INSERT_BACK);
struct ir_value new_val;
new_val.type = IR_VALUE_INSN;
new_val.data.insn_d = new_insn;
write_variable(env, insn.dst_reg, bb, new_val);
}

else {
// TODO
CRITICAL("Error");
}

} else if (BPF_CLASS(code) == BPF_LD && BPF_MODE(code) == BPF_IMM &&
BPF_SIZE(code) == BPF_DW) {
// 64-bit immediate load
// TODO
if (insn.src_reg == 0x0) {
// immediate value
struct ir_value imm_val;
imm_val.type = IR_VALUE_CONSTANT;
imm_val.data.constant_d.type = IR_CONSTANT_U64;
imm_val.data.constant_d.data.u64_d = insn.imm64;
write_variable(env, insn.dst_reg, bb, imm_val);
} else {
CRITICAL("Not supported");
}
} else if (BPF_CLASS(code) == BPF_LDX && BPF_MODE(code) == BPF_MEMSX) {
// dst = *(signed size *) (src + offset)
// https://www.kernel.org/doc/html/v6.6/bpf/standardization/instruction-set.html#sign-extension-load-operations
Expand Down Expand Up @@ -628,10 +647,7 @@ void transform_bb(struct ssa_transform_env *env, struct pre_ir_basic_block *bb)
new_insn->bb1 = get_ir_bb_from_position(env, pos);
} else if (BPF_OP(code) == BPF_EXIT) {
// Exit
struct ir_insn *new_insn = create_insn_back(bb->ir_bb);
new_insn->op = IR_INSN_RET;
new_insn->values[0] = read_variable(env, BPF_REG_0, bb);
new_insn->value_num = 1;
create_ret_insn_bb(bb->ir_bb, read_variable(env, BPF_REG_0, bb), INSERT_BACK);
} else if (BPF_OP(code) == BPF_JEQ) {
// PC += offset if dst == src
struct ir_insn *new_insn = create_insn_back(bb->ir_bb);
Expand Down Expand Up @@ -769,11 +785,11 @@ void free_function(struct ir_function *fun) {
}

struct ir_function gen_function(struct ssa_transform_env *env) {
struct ir_function func;
func.arg_num = 1;
func.entry = env->info.entry->ir_bb;
func.sp_users = env->sp_users;
func.all_bbs = array_init(sizeof(struct ir_basic_block *));
struct ir_function fun;
fun.arg_num = 1;
fun.entry = env->info.entry->ir_bb;
fun.sp_users = env->sp_users;
fun.all_bbs = array_init(sizeof(struct ir_basic_block *));
for (size_t i = 0; i < MAX_BPF_REG; ++i) {
struct array *currentDef = &env->currentDef[i];
array_free(currentDef);
Expand All @@ -784,10 +800,10 @@ struct ir_function gen_function(struct ssa_transform_env *env) {
array_free(&bb->succs);
free(bb->pre_insns);
bb->ir_bb->user_data = NULL;
array_push(&func.all_bbs, &bb->ir_bb);
array_push(&fun.all_bbs, &bb->ir_bb);
free(bb);
}
return func;
return fun;
}

__u8 ir_value_equal(struct ir_value a, struct ir_value b) {
Expand Down Expand Up @@ -836,6 +852,11 @@ void run(struct bpf_insn *insns, size_t len) {
printf("--------------------\n");
print_ir_prog(&fun);

// Test
// add_stack_offset(&fun, -8);
// printf("--------------------\n");
// print_ir_prog(&fun);

// Free the memory
free_function(&fun);
}
3 changes: 3 additions & 0 deletions IR/format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

find . -iname '*.h' -o -iname '*.c' | xargs clang-format -i
8 changes: 8 additions & 0 deletions IR/include/add_constraint_pass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef __BPF_IR_ADD_CONSTRAINT_PASS_H__
#define __BPF_IR_ADD_CONSTRAINT_PASS_H__

#include "ir_fun.h"

void add_constraint(struct ir_function *fun);

#endif
2 changes: 1 addition & 1 deletion IR/include/add_counter_pass.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef __BPF_IR_ADD_COUNTER_PASS_H__
#define __BPF_IR_ADD_COUNTER_PASS_H__

#include "bpf_ir.h"
#include "ir_fun.h"

void add_counter(struct ir_function *fun);

Expand Down
9 changes: 9 additions & 0 deletions IR/include/add_stack_offset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef __BPF_IR_ADD_STACK_OFFSET_H__
#define __BPF_IR_ADD_STACK_OFFSET_H__

#include "ir_fun.h"

// Add stack offset to all stack access
void add_stack_offset(struct ir_function *fun, __s16 offset);

#endif
2 changes: 2 additions & 0 deletions IR/include/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ void __free(void *ptr);
#define array_for(pos, arr) \
for (pos = ((typeof(pos))(arr.data)); pos < (typeof(pos))(arr.data) + arr.num_elem; pos++)

#define INIT_ARRAY(type) array_init(sizeof(type))

#endif
Loading