Created
January 31, 2021 08:38
-
-
Save dvyukov/e5c0a8ef220ef856363c1080b0936a9e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <bcc/libbpf.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <linux/bpf.h> | |
#include <linux/bpf_perf_event.h> | |
#include <linux/hw_breakpoint.h> | |
#include <linux/perf_event.h> | |
#include <pthread.h> | |
#include <signal.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/stat.h> | |
#include <sys/syscall.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
char heap[10]; | |
int pipefd[2]; | |
int stop = 0; | |
void* racer(void* arg) | |
{ | |
unsigned rnd = syscall(SYS_gettid); | |
for (unsigned i = 0; !__atomic_load_n(&stop, __ATOMIC_RELAXED); i++) { | |
char* addr = heap + rand_r(&rnd) % sizeof(heap); | |
switch (rand_r(&rnd) % 3) { | |
case 0: | |
*(volatile char*)addr; | |
break; | |
case 1: | |
*(volatile char*)addr = 1; | |
break; | |
case 2: | |
if (write(pipefd[1], addr, 1) < 0) | |
_exit(printf("write: %s\n", strerror(errno))); | |
if (read(pipefd[0], addr, 1) < 0) | |
_exit(printf("read: %s\n", strerror(errno))); | |
break; | |
} | |
} | |
return NULL; | |
} | |
int main() | |
{ | |
if (pipe(pipefd)) | |
_exit(printf("pipe: %s\n", strerror(errno))); | |
pthread_t th[4]; | |
for (int i = 0; i < sizeof(th) / sizeof(th[0]); i++) { | |
if (pthread_create(&th[i], NULL, racer, NULL)) | |
_exit(printf("pthread_create: %s\n", strerror(errno))); | |
} | |
sleep(2); | |
__atomic_store_n(&stop, 1, __ATOMIC_RELAXED); | |
for (int i = 0; i < sizeof(th) / sizeof(th[0]); i++) { | |
if (pthread_join(th[i], NULL)) | |
_exit(printf("pthread_join: %s\n", strerror(errno))); | |
} | |
return 0; | |
} | |
enum bp_state { | |
bp_disabled, | |
bp_armed, | |
bp_triggered, | |
}; | |
struct breakpoint_t { | |
int fd; | |
enum bp_state state; | |
void* addr; | |
struct access_t* access; | |
}; | |
struct context_t { | |
int addr_map_fd; | |
unsigned rnd; | |
struct breakpoint_t bp[4]; | |
}; | |
static struct context_t ctx; | |
static struct perf_event_attr init_perf_event_addr(bool enabled, bool rw, void* addr) | |
{ | |
struct perf_event_attr attr = { | |
.type = PERF_TYPE_BREAKPOINT, | |
.size = sizeof(attr), | |
.sample_period = 1, | |
.disabled = !enabled, | |
.inherit = 1, | |
.bp_addr = (long)addr, | |
.bp_type = rw ? HW_BREAKPOINT_RW : HW_BREAKPOINT_W, | |
.bp_len = HW_BREAKPOINT_LEN_1, | |
}; | |
return attr; | |
} | |
static void rearm(struct breakpoint_t* bp) | |
{ | |
if (bp->state == bp_triggered) | |
return; | |
void* new_addr = heap + rand_r(&ctx.rnd) % sizeof(heap); | |
struct perf_event_attr attr = init_perf_event_addr(true, false, new_addr); | |
if (ioctl(bp->fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr)) | |
_exit(printf("ioctl(PERF_EVENT_IOC_MODIFY_ATTRIBUTES): %s\n", strerror(errno))); | |
bp->state = bp_armed; | |
bp->addr = new_addr; | |
} | |
static void* thread(void* arg) | |
{ | |
int pid = syscall(SYS_getpid); | |
for (unsigned i = 0;; i++) { | |
rearm(&ctx.bp[i % (sizeof(ctx.bp) / sizeof(ctx.bp[0]))]); | |
usleep(100000); | |
} | |
return NULL; | |
} | |
static void init() | |
{ | |
union bpf_attr map_attr = { | |
.map_type = BPF_MAP_TYPE_HASH, | |
.key_size = sizeof(pid_t), | |
.value_size = sizeof(void*), | |
.max_entries = 10, | |
}; | |
ctx.addr_map_fd = syscall(SYS_bpf, BPF_MAP_CREATE, &map_attr, sizeof(map_attr)); | |
if (ctx.addr_map_fd == -1) | |
_exit(printf("bpf(BPF_MAP_CREATE): %s\n", strerror(errno))); | |
char log_buf[1024] = {0}; | |
struct bpf_insn insns[] = { | |
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_current_pid_tgid), | |
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -8), | |
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | |
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | |
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_6, offsetof(struct bpf_perf_event_data, addr)), | |
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, -16), | |
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), | |
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -16), | |
BPF_LD_MAP_FD(BPF_REG_1, ctx.addr_map_fd), | |
BPF_MOV64_IMM(BPF_REG_4, 0), | |
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem), | |
BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0), | |
BPF_EXIT_INSN(), | |
}; | |
union bpf_attr bpf_attr = { | |
.prog_type = BPF_PROG_TYPE_PERF_EVENT, | |
.insn_cnt = sizeof(insns) / sizeof(insns[0]), | |
.insns = (long)insns, | |
.license = (long)"", | |
.log_level = 1, | |
.log_size = sizeof(log_buf), | |
.log_buf = (long)log_buf, | |
}; | |
int prog_fd = syscall(SYS_bpf, BPF_PROG_LOAD, &bpf_attr, sizeof(bpf_attr)); | |
if (prog_fd == -1) | |
_exit(printf("bpf(BPF_PROG_LOAD): %s\n%s\n", strerror(errno), log_buf)); | |
for (int i = 0; i < sizeof(ctx.bp) / sizeof(ctx.bp[0]); i++) { | |
struct perf_event_attr attr = init_perf_event_addr(false, false, (void*)(long)i); | |
ctx.bp[i].fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC); | |
if (ctx.bp[i].fd == -1) | |
_exit(printf("perf_event_open: %s\n", strerror(errno))); | |
if (ioctl(ctx.bp[i].fd, PERF_EVENT_IOC_SET_BPF, prog_fd)) | |
_exit(printf("ioctl(PERF_EVENT_IOC_SET_BPF): %s\n", strerror(errno))); | |
} | |
close(prog_fd); | |
pthread_t th; | |
if (pthread_create(&th, NULL, thread, NULL)) | |
_exit(printf("pthread_create: %s\n", strerror(errno))); | |
if (pthread_detach(th)) | |
_exit(printf("pthread_detach: %s\n", strerror(errno))); | |
} | |
__attribute__((section(".preinit_array"), used)) void (*__preinit)(void) = init; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment