diff --git a/include/ply/buffer.h b/include/ply/buffer.h index d4df3e9..43ed4f7 100644 --- a/include/ply/buffer.h +++ b/include/ply/buffer.h @@ -8,6 +8,7 @@ #define _PLY_BUFFER_H #include +#include #include @@ -34,8 +35,10 @@ void buffer_evh_register(struct buffer_evh *evh); struct buffer; -struct buffer *buffer_new(int mapfd); +struct ply_return buffer_service(struct buffer *buf, int ready, struct pollfd *fds); +void buffer_fill_pollset(struct buffer *buf, struct pollfd *fds); +nfds_t buffer_get_nfds(struct buffer *buf); -struct ply_return buffer_loop(struct buffer *buf, int timeout); +struct buffer *buffer_new(int mapfd); #endif /* _PLY_BUFFER_H */ diff --git a/include/ply/ir.h b/include/ply/ir.h index 72d208d..2584183 100644 --- a/include/ply/ir.h +++ b/include/ply/ir.h @@ -148,6 +148,7 @@ struct irstate { int dot:1; int lval:1; int stack:1; + int user:1; } hint; }; @@ -177,7 +178,7 @@ void ir_emit_sym_to_reg (struct ir *ir, uint16_t dst, struct sym *src); void ir_emit_reg_to_sym (struct ir *ir, struct sym *dst, uint16_t src); void ir_emit_sym_to_stack(struct ir *ir, ssize_t offset, struct sym *src); void ir_emit_sym_to_sym (struct ir *ir, struct sym *dst, struct sym *src); -void ir_emit_read_to_sym (struct ir *ir, struct sym *dst, uint16_t src); +void ir_emit_read_to_sym (struct ir *ir, struct sym *dst, uint16_t src, int user); void ir_emit_data (struct ir *ir, ssize_t dst, const char *src, size_t size); void ir_emit_memcpy(struct ir *ir, ssize_t dst, ssize_t src, size_t size); diff --git a/include/ply/ply.h b/include/ply/ply.h index 7f38a76..8e37493 100644 --- a/include/ply/ply.h +++ b/include/ply/ply.h @@ -8,6 +8,7 @@ #define _PLY_H #include +#include #include "sym.h" #include "utils.h" @@ -23,6 +24,22 @@ struct ply_return { unsigned exit:1; }; +static inline void ply_return_fold(struct ply_return *ret, struct ply_return new) +{ + if (ret->err) + return; + + if (new.err) { + *ret = new; + return; + } + + if (ret->exit) + return; + + *ret = new; +} + /* api */ struct ply_probe { struct ply_probe *next, *prev; @@ -83,7 +100,9 @@ void ply_maps_print(struct ply *ply); void ply_map_print(struct ply *ply, struct sym *sym, FILE *fp); void ply_map_clear(struct ply *ply, struct sym *sym); -struct ply_return ply_loop(struct ply *ply); +struct ply_return ply_service(struct ply *ply, int ready, struct pollfd *fds); +nfds_t ply_get_nfds(struct ply *ply); +void ply_fill_pollset(struct ply *ply, struct pollfd *fds); int ply_start(struct ply *ply); int ply_stop(struct ply *ply); diff --git a/include/ply/provider.h b/include/ply/provider.h index 67709a1..7db0e89 100644 --- a/include/ply/provider.h +++ b/include/ply/provider.h @@ -26,6 +26,8 @@ struct provider { int (*ir_pre) (struct ply_probe *); int (*ir_post) (struct ply_probe *); int (*attach) (struct ply_probe *); + int (*start) (struct ply_probe *); + int (*stop) (struct ply_probe *); int (*detach) (struct ply_probe *); }; diff --git a/src/libply/built-in/buffer.c b/src/libply/built-in/buffer.c index 541e051..36bd7ad 100644 --- a/src/libply/built-in/buffer.c +++ b/src/libply/built-in/buffer.c @@ -153,43 +153,7 @@ struct ply_return buffer_q_drain(struct buffer_q *q) return ret; } -struct ply_return buffer_loop(struct buffer *buf, int timeout) -{ - struct ply_return ret; - uint32_t cpu; - int ready; - - for (;;) { - ready = poll(buf->poll, buf->ncpus, timeout); - if (ready < 0) { - ret.err = 1; - ret.val = errno; - return ret; - } - - if (timeout == -1) { - assert(ready); - } else if (ready == 0) { - ret.err = 0; - return ret; - } - - for (cpu = 0; ready && (cpu < buf->ncpus); cpu++) { - if (!(buf->poll[cpu].revents & POLLIN)) - continue; - - ret = buffer_q_drain(&buf->q[cpu]); - if (ret.err | ret.exit) - return ret; - - ready--; - } - } - - return ret; -} - -int buffer_q_init(struct buffer *buf, uint32_t cpu) +static int buffer_q_init(struct buffer *buf, uint32_t cpu) { struct perf_event_attr attr = { 0 }; struct buffer_q *q = &buf->q[cpu]; @@ -225,6 +189,34 @@ int buffer_q_init(struct buffer *buf, uint32_t cpu) return 0; } +struct ply_return buffer_service(struct buffer *buf, int ready, struct pollfd *fds) +{ + struct ply_return ret = {}; + uint32_t cpu; + + for (cpu = 0; ready && (cpu < buf->ncpus); cpu++) { + if (!(fds[cpu].revents & POLLIN)) + continue; + + ret = buffer_q_drain(&buf->q[cpu]); + ready--; + if (ret.err || ret.exit) + break; + } + + return ret; +} + +void buffer_fill_pollset(struct buffer *buf, struct pollfd *fds) +{ + memcpy(fds, buf->poll, buf->ncpus * sizeof(*fds)); +} + +nfds_t buffer_get_nfds(struct buffer *buf) +{ + return buf->ncpus; +} + struct buffer *buffer_new(int mapfd) { struct buffer *buf; diff --git a/src/libply/built-in/memory.c b/src/libply/built-in/memory.c index fe222b5..ff64a65 100644 --- a/src/libply/built-in/memory.c +++ b/src/libply/built-in/memory.c @@ -140,7 +140,10 @@ static int str_ir_post(const struct func *func, struct node *n, ir_emit_ldbp(pb->ir, BPF_REG_1, n->sym->irs.stack); ir_emit_insn(ir, MOV_IMM((int32_t)type_sizeof(n->sym->type)), BPF_REG_2, 0); ir_emit_sym_to_reg(ir, BPF_REG_3, ptr->sym); - ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_kernel_str), 0, 0); + if (ptr->sym->irs.hint.user) + ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_user_str), 0, 0); + else + ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_kernel_str), 0, 0); return 0; } @@ -153,7 +156,7 @@ static int mem_ir_post(const struct func *func, struct node *n, ir_init_sym(pb->ir, n->sym); ir_emit_sym_to_reg(pb->ir, BPF_REG_3, ptr->sym); - ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_3); + ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_3, ptr->sym->irs.hint.user); return 0; } @@ -222,6 +225,36 @@ static struct func str_func = { .ir_post = str_ir_post, }; +struct type t_uptr_func = { + .ttype = T_FUNC, + .func = { .type = &t_void, .args = f_1arg }, +}; + +static int uptr_ir_post(const struct func *func, struct node *n, + struct ply_probe *pb) +{ + struct node *child = n->expr.args; + + ir_init_sym(pb->ir, n->sym); + ir_emit_sym_to_sym(pb->ir, n->sym, child->sym); + n->sym->irs.hint.user = 1; + return 0; +} + +static int uptr_type_infer(const struct func *func, struct node *n) +{ + struct node *arg = n->expr.args; + + n->sym->type = arg->sym->type; + return 0; +} + +static struct func uptr_func = { + .name = "uptr", + .type = &t_uptr_func, + .type_infer = uptr_type_infer, + .ir_post = uptr_ir_post, +}; static int struct_deref_rewrite(const struct func *func, struct node *n, struct ply_probe *pb) @@ -424,7 +457,7 @@ static int deref_ir_post(const struct func *func, struct node *n, return 0; ir_emit_sym_to_reg(pb->ir, BPF_REG_0, ptr->sym); - ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_0); + ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_0, ptr->sym->irs.hint.user); return 0; } @@ -603,7 +636,7 @@ static int map_ir_post(const struct func *func, struct node *n, lhit = ir_alloc_label(pb->ir); ir_emit_insn(pb->ir, JMP_IMM(BPF_JEQ, 0, lmiss), BPF_REG_0, 0); - ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_0); + ir_emit_read_to_sym(pb->ir, n->sym, BPF_REG_0, 0); ir_emit_insn(pb->ir, JMP_IMM(BPF_JA, 0, lhit), 0, 0); ir_emit_label(pb->ir, lmiss); @@ -948,6 +981,7 @@ void memory_init(void) built_in_register(&strcmp_func); built_in_register(&str_func); built_in_register(&mem_func); + built_in_register(&uptr_func); built_in_register(&struct_deref_func); built_in_register(&struct_dot_func); built_in_register(&deref_func); diff --git a/src/libply/grammar.y b/src/libply/grammar.y index 792a732..9790bf3 100644 --- a/src/libply/grammar.y +++ b/src/libply/grammar.y @@ -61,8 +61,8 @@ probes ; probe -: PSPEC stmt { __ply_probe_alloc(ply, $1, $2); } -| PSPEC predicate { __ply_probe_alloc(ply, $1, $2); } +: PSPEC stmt { if (__ply_probe_alloc(ply, $1, $2)) YYABORT; } +| PSPEC predicate { if (__ply_probe_alloc(ply, $1, $2)) YYABORT; } ; /* Support dtrace-style predicates as well as normal if guards. I.e. diff --git a/src/libply/ir.c b/src/libply/ir.c index 8c546ab..f7f38d8 100644 --- a/src/libply/ir.c +++ b/src/libply/ir.c @@ -42,6 +42,10 @@ static const char *bpf_func_name(enum bpf_func_id id) return "probe_read_kernel"; case BPF_FUNC_probe_read_kernel_str: return "probe_read_kernel_str"; + case BPF_FUNC_probe_read_user: + return "probe_read_user"; + case BPF_FUNC_probe_read_user_str: + return "probe_read_user_str"; case BPF_FUNC_trace_printk: return "trace_printk"; default: @@ -405,7 +409,7 @@ void ir_emit_sym_to_sym(struct ir *ir, struct sym *dst, struct sym *src) } } -void ir_emit_read_to_sym(struct ir *ir, struct sym *dst, uint16_t src) +void ir_emit_read_to_sym(struct ir *ir, struct sym *dst, uint16_t src, int user) { struct irstate *irs = &dst->irs; @@ -416,7 +420,10 @@ void ir_emit_read_to_sym(struct ir *ir, struct sym *dst, uint16_t src) if (src != BPF_REG_3) ir_emit_insn(ir, MOV, BPF_REG_3, src); - ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_kernel), 0, 0); + if (user) + ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_user), 0, 0); + else + ir_emit_insn(ir, CALL(BPF_FUNC_probe_read_kernel), 0, 0); /* TODO if (r0) exit(r0); */ } diff --git a/src/libply/libply.c b/src/libply/libply.c index 2141cc2..0e86e50 100644 --- a/src/libply/libply.c +++ b/src/libply/libply.c @@ -243,7 +243,7 @@ static int ply_unload_detach(struct ply *ply) int err; ply_probe_foreach(ply, pb) { - if (pb->special) + if (!pb->provider->detach) continue; err = pb->provider->detach(pb); @@ -259,21 +259,8 @@ int ply_unload(struct ply *ply) struct ply_probe *pb; int err; - /* run END probe at last */ - ply_probe_foreach(ply, pb) { - if (!pb->special || strcmp(pb->provider->name, "END")) - continue; - - err = bpf_prog_test_run(pb->bpf_fd); - if (err) - return err; - - /* read buffer again for END trigger */ - if (ply->stdbuf) - buffer_loop((struct buffer *)ply->stdbuf->priv, 0); - } - - err = ply_unload_bpf(ply); + err = ply_unload_detach(ply); + err |= ply_unload_bpf(ply); err |= ply_unload_map(ply); return err; } @@ -382,23 +369,7 @@ static struct ply_return ply_load_attach(struct ply *ply) int err; ply_probe_foreach(ply, pb) { - if (!pb->special || strcmp(pb->provider->name, "BEGIN")) - continue; - - err = bpf_prog_test_run(pb->bpf_fd); - if (err) - goto err; - - /* read buffer for BEGIN trigger */ - if (ply->stdbuf) { - ret = buffer_loop((struct buffer *)ply->stdbuf->priv, 0); - if (ret.exit || ret.err) - return ret; - } - } - - ply_probe_foreach(ply, pb) { - if (pb->special) + if (!pb->provider->attach) continue; err = pb->provider->attach(pb); @@ -448,14 +419,26 @@ struct ply_return ply_load(struct ply *ply) } -struct ply_return ply_loop(struct ply *ply) +struct ply_return ply_service(struct ply *ply, int ready, struct pollfd *fds) { - if (!ply->stdbuf) { - pause(); - return (struct ply_return){ .err = 1, .val = EINTR }; - } + if (ply->stdbuf) + return buffer_service(ply->stdbuf->priv, ready, fds); + + return (struct ply_return){ .err = 0, .val = 0 }; +} + +nfds_t ply_get_nfds(struct ply *ply) +{ + if (ply->stdbuf) + return buffer_get_nfds(ply->stdbuf->priv); + + return 0; +} - return buffer_loop((struct buffer *)ply->stdbuf->priv, -1); +void ply_fill_pollset(struct ply *ply, struct pollfd *fds) +{ + if (ply->stdbuf) + buffer_fill_pollset(ply->stdbuf->priv, fds); } int ply_stop(struct ply *ply) @@ -469,19 +452,32 @@ int ply_stop(struct ply *ply) return err; } - err = ply_unload_detach(ply); - if (err) - return err; + ply_probe_foreach(ply, pb) { + if (!pb->provider->stop) + continue; - /* flush existing buffer entries */ - if (ply->stdbuf) - buffer_loop((struct buffer *)ply->stdbuf->priv, 0); + err = pb->provider->stop(pb); + if (err) + return err; + } return 0; } int ply_start(struct ply *ply) { + struct ply_probe *pb; + int err; + + ply_probe_foreach(ply, pb) { + if (!pb->provider->start) + continue; + + err = pb->provider->start(pb); + if (err) + return err; + } + if (ply->group_fd < 0) return 0; diff --git a/src/libply/provider/special.c b/src/libply/provider/special.c index bc4f9b5..cc99d6c 100644 --- a/src/libply/provider/special.c +++ b/src/libply/provider/special.c @@ -9,6 +9,11 @@ #include #include +static int special_exec(struct ply_probe *pb) +{ + return bpf_prog_test_run(pb->bpf_fd); +} + static int special_sym_alloc(struct ply_probe *pb, struct node *n) { return -ENOENT; @@ -26,6 +31,8 @@ struct provider begin_provider = { .probe = special_probe, .sym_alloc = special_sym_alloc, + + .start = special_exec, }; struct provider end_provider = { @@ -34,4 +41,6 @@ struct provider end_provider = { .probe = special_probe, .sym_alloc = special_sym_alloc, + + .stop = special_exec, }; diff --git a/src/ply/ply.c b/src/ply/ply.c index 950540d..d7fff80 100644 --- a/src/ply/ply.c +++ b/src/ply/ply.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include +#include #include #include @@ -120,8 +122,10 @@ void dump(struct ply *ply) symtab_dump(&ply->globals, stdout); ply_probe_foreach(ply, pb) { + printf("\n\n-- probe\n"); printf("%s\n", pb->probe ? : ""); + printf("\n\n-- ast\n"); if (pb->ast) ast_fprint(stdout, pb->ast); else @@ -213,25 +217,78 @@ int inferior_prep(const char *cmd, int *infpid, int *inftrig) return execl("/bin/sh", "sh", "-c", cmd, NULL); } -static int term_sig = 0; -static void term(int sig) +static sigset_t orig_sigmask; + +struct ply_return sig_handle(int fd) +{ + struct signalfd_siginfo si; + int status; + + if (read(fd, &si, sizeof(si)) != sizeof(si)) { + return (struct ply_return) { + .err = 1, + .val = -EIO, + }; + } + + switch (si.ssi_signo) { + case SIGCHLD: + _d("SIGCHLD\n"); + waitpid(0, &status, WNOHANG); + return (struct ply_return) { + .exit = 0, + .val = 0, + }; + case SIGINT: + _d("SIGINT\n"); + return (struct ply_return) { + .exit = 1, + .val = 1, + }; + } + + _e("Unexpected signal: %u\n", si.ssi_signo); + return (struct ply_return) { + .err = 1, + .val = -EINVAL, + }; +} + +void sig_exit(int fd) { - term_sig = sig; - return; + close(fd); + sigprocmask(SIG_SETMASK, &orig_sigmask, NULL); +} + +int sig_init(void) +{ + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &mask, &orig_sigmask) == -1) + return -errno; + + fd = signalfd(-1, &mask, SFD_CLOEXEC); + if (fd == -1) + return -errno; + + return fd; } -static const struct sigaction term_action = { - .sa_handler = term, - .sa_flags = 0, -}; int main(int argc, char **argv) { - struct ply *ply; struct ply_return ret = { .err = 1 }; - int opt, infpid, inftrig; + int opt, infpid, inftrig, sfd, ready; + struct pollfd *fds = NULL; int f_dryrun, f_dump; - FILE *src; char *cmd = NULL; + struct ply *ply; + nfds_t nfds; + FILE *src; f_dryrun = f_dump = 0; while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) > 0) { @@ -304,18 +361,46 @@ int main(int argc, char **argv) if (f_dryrun) goto unload; + sfd = sig_init(); + if (sfd < 0) { + ret.val = sfd; + goto err; + } + + fds = malloc(sizeof(*fds)); + if (!fds) { + ret.val = -ENOMEM; + goto err; + } + + *fds = (struct pollfd) { + .fd = sfd, + .events = POLLIN, + }; + memlock_uncap(); ret = ply_load(ply); if (ret.exit || ret.err) goto err; + nfds = ply_get_nfds(ply); + if (nfds) { + fds = realloc(fds, (1 + nfds) * sizeof(*fds)); + if (!fds) { + ret = (struct ply_return) { + .err = 1, + .val = -ENOMEM, + }; + goto err; + } + + ply_fill_pollset(ply, &fds[1]); + } + ply_start(ply); _d("ply: active\n"); - sigaction(SIGINT, &term_action, NULL); - sigaction(SIGCHLD, &term_action, NULL); - if (cmd) { int err = 0; @@ -327,13 +412,42 @@ int main(int argc, char **argv) } } - ret = ply_loop(ply); - if (ret.err && (ret.val == EINTR) && term_sig) - ret.err = 0; + for (;;) { + ready = poll(fds, 1 + nfds, -1); + if (ready < 0) { + ret = (struct ply_return) { + .err = 1, + .val = errno, + }; + break; + } + + if (fds[0].revents & POLLIN) { + ret = sig_handle(sfd); + if (--ready == 0) + break; + } + + ply_return_fold(&ret, ply_service(ply, ready, &fds[1])); + if (ret.err || ret.exit) + break; + } + + sig_exit(sfd); + stop: _d("ply: deactivating\n"); ply_stop(ply); + /* END probes may generate events, so do a final poll for them + * before shutting down. + */ + ready = poll(&fds[1], nfds, 0); + if (ready > 0) + ply_return_fold(&ret, ply_service(ply, ready, &fds[1])); + + free(fds); + ply_maps_print(ply); unload: diff --git a/test/rootfs/lib/ply/test.sh b/test/rootfs/lib/ply/test.sh index 8135a6f..4a36e7f 100755 --- a/test/rootfs/lib/ply/test.sh +++ b/test/rootfs/lib/ply/test.sh @@ -2,6 +2,11 @@ total_fails=0 +has_syscall() +{ + [ -e /sys/kernel/debug/tracing/events/syscalls/sys_enter_$1 ] +} + atomics_supported() { case $(uname -m) in @@ -43,7 +48,7 @@ case=print && ply_simple 'print("test"); exit(0);' && \ case=wildcard ply -c \ - "dd if=/dev/zero of=/dev/null bs=1 count=100" \ + "dd if=/dev/zero of=/dev/null bs=1 count=100 status=none" \ "kprobe:vfs_*r[ei][at][de] { @[comm, caller] = count(); }" >/tmp/wildcard \ && \ cat /tmp/wildcard | awk ' @@ -56,7 +61,7 @@ cat /tmp/wildcard | awk ' if atomics_supported; then case=quantize ply -c \ - "dd if=/dev/zero of=/dev/null bs=10240 count=10" \ + "dd if=/dev/zero of=/dev/null bs=10240 count=10 status=none" \ 'kr:vfs_read if (!strcmp(comm, "dd")) { @["rdsz"] = quantize(retval); }' >/tmp/quantize \ @@ -66,7 +71,7 @@ if atomics_supported; then fi case=interval -ply -c 'for i in `seq 3`; do dd if=/dev/zero of=/dev/null count=10; sleep 1; done' \ +ply -c 'for i in `seq 3`; do dd if=/dev/zero of=/dev/null count=10 status=none; sleep 1; done' \ 'k:vfs_read { @[pid] = count(); } i:1 { print(@); clear(@); }' >/tmp/interval \ && \ @@ -99,4 +104,23 @@ cat /tmp/profile | awk -F': ' ' END { exit(count != 100); }' \ || fail "count should be 100 for profile provider test" "$(cat /tmp/profile)" +case=uptr + +# Check for all variants of open(2). NOTE: The original open() is +# _not_ always available (aarch64). +probes= +for open in open openat openat2; do + has_syscall $open || continue + + probes="${probes} + tracepoint:syscalls/sys_enter_${open} { + print(\"${open}\", str(uptr(data->filename))); + }" +done + +ply -c 'cat /etc/hostname >/dev/null' "${probes}" >/tmp/uptr \ +&& \ +grep -qe "/etc/hostname" /tmp/uptr \ +|| fail "open{,at,at2} , /etc/hostname" "$(cat /tmp/uptr)" + exit $total_fails