Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@dvyukov
Created January 31, 2021 08:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvyukov/e5c0a8ef220ef856363c1080b0936a9e to your computer and use it in GitHub Desktop.
Save dvyukov/e5c0a8ef220ef856363c1080b0936a9e to your computer and use it in GitHub Desktop.
#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