Formalize requirements to evaluation events in documentation and adjust their handling in the actual code to match.
Particularly, specify more precisely: * conditions for each catchpoint callback; * relative order of callbacks within the step; * expected value of the program counter; Behaviour changes: * one branch-return event is now generated for each branch-enter event (previously multiple branch-return events could be coalesced); * branch-return event now points to the corresponding conditional jump (previously past the end of the last branch); * step event is now only generated before an actual instruction evaluation (previously also for branching and finishing events); * program counter is no longer allowed to point past the program end; These changes should simplify branch tracking of the validator by tests and interactive applications. E.g. branch-enter and branch-return events can now be used to push and pop branching points to/from some stack. Signed-off-by: Marat Khalili <[email protected]> --- lib/bpf/bpf_validate.c | 28 +++++++++---- lib/bpf/bpf_validate_debug.c | 72 +++++++++++++++++++------------- lib/bpf/bpf_validate_debug.h | 3 +- lib/bpf/rte_bpf_validate_debug.h | 24 +++++++---- 4 files changed, 78 insertions(+), 49 deletions(-) diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c index b317abb81172..f4258c2fb54f 100644 --- a/lib/bpf/bpf_validate.c +++ b/lib/bpf/bpf_validate.c @@ -2892,6 +2892,23 @@ prune_eval_state(struct bpf_verifier *bvf, const struct inst_node *node, return rc; } +static bool +is_branch_start(const struct inst_node *node) +{ + return node->prev_node != NULL && node->prev_node->nb_edge > 1; +} + +static uint64_t +step_events(const struct inst_node *node) +{ + uint64_t events = RTE_BIT64(RTE_BPF_VALIDATE_DEBUG_EVENT_STEP); + + if (is_branch_start(node)) + events |= RTE_BIT64(RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_ENTER); + + return events; +} + /* Do second pass through CFG and try to evaluate instructions * via each possible path. The verifier will try all paths, tracking types of * registers used as input to instructions, and updating resulting type via @@ -2918,7 +2935,6 @@ evaluate(struct bpf_verifier *bvf) const char *err; const struct ebpf_insn *ins; struct inst_node *next, *node; - int prev_nb_edge; /* branching number of the previous instruction */ int rc, debug_rc; struct rte_bpf_validate_debug *const debug = bvf->prm->debug; @@ -2954,7 +2970,6 @@ evaluate(struct bpf_verifier *bvf) ins = bvf->prm->raw.ins; node = bvf->in; next = node; - prev_nb_edge = 1; memset(&stats, 0, sizeof(stats)); @@ -2990,8 +3005,7 @@ evaluate(struct bpf_verifier *bvf) } rc = __rte_bpf_validate_debug_evaluate_update(debug, idx, - prev_nb_edge > 1 ? - RTE_BIT64(RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_ENTER) : 0); + step_events(node)); if (rc < 0) break; @@ -3047,7 +3061,6 @@ evaluate(struct bpf_verifier *bvf) stats.nb_prune++; } else { next->prev_node = node; - prev_nb_edge = node->nb_edge; node = next; } } else { @@ -3057,9 +3070,9 @@ evaluate(struct bpf_verifier *bvf) * and proceed with parent. */ - if (prev_nb_edge != 0) { + if (is_branch_start(node)) { rc = __rte_bpf_validate_debug_evaluate_update( - debug, get_node_idx(bvf, node) + 1, + debug, get_node_idx(bvf, node->prev_node), RTE_BIT64(RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_RETURN)); if (rc < 0) break; @@ -3067,7 +3080,6 @@ evaluate(struct bpf_verifier *bvf) node->cur_edge = 0; save_safe_eval_state(bvf, node); - prev_nb_edge = 0; node = node->prev_node; /* first node will not have prev, signalling finish */ diff --git a/lib/bpf/bpf_validate_debug.c b/lib/bpf/bpf_validate_debug.c index f76303a7fd2a..db6d13f9cb22 100644 --- a/lib/bpf/bpf_validate_debug.c +++ b/lib/bpf/bpf_validate_debug.c @@ -225,6 +225,13 @@ debug_points_call_back(struct rte_bpf_validate_debug *debug, return rc; } +/* Call back all breakpoints for the specified program counter. */ +static int +debug_trigger_breakpoints(struct rte_bpf_validate_debug *debug, uint32_t pc) +{ + return debug_points_call_back(debug, &debug->breakpoint_lists[pc]); +} + /* Call back all catchpoints for the specified event. */ static int debug_send_event(struct rte_bpf_validate_debug *debug, debug_event_t event) @@ -585,6 +592,21 @@ int __rte_bpf_validate_debug_evaluate_update(struct rte_bpf_validate_debug *debug, uint32_t pc, uint64_t events) { + /* Required order of sent events according to the documentation. */ + static const enum rte_bpf_validate_debug_event ordered_events[] = { + RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_START, + RTE_BPF_VALIDATE_DEBUG_EVENT_INVALID_STATE, + RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_ENTER, + RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_PRUNE, + RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_RETURN, + RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_UNREACHABLE, + RTE_BPF_VALIDATE_DEBUG_EVENT_STEP, + RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_SUCCESS, + RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_FAILURE, + }; + RTE_BUILD_BUG_ON( + RTE_DIM(ordered_events) != RTE_BPF_VALIDATE_DEBUG_EVENT_END); + int rc; if (debug == NULL) @@ -595,29 +617,31 @@ __rte_bpf_validate_debug_evaluate_update(struct rte_bpf_validate_debug *debug, return -ECHILD; } - if (pc > debug->bpf_prm->raw.nb_ins) + if (pc >= debug->bpf_prm->raw.nb_ins) return -EINVAL; debug->pc = pc; rc = __rte_bpf_validate_state_is_valid(debug->verifier); if (rc == 0) - rc = debug_send_event(debug, - RTE_BPF_VALIDATE_DEBUG_EVENT_INVALID_STATE); + events |= RTE_BIT64(RTE_BPF_VALIDATE_DEBUG_EVENT_INVALID_STATE); + + for (uint32_t index = 0; index < RTE_DIM(ordered_events); index++) { + const enum rte_bpf_validate_debug_event event = + ordered_events[index]; + if ((events & RTE_BIT64(event)) == 0) + continue; - for (enum rte_bpf_validate_debug_event event = 0; - event != RTE_BPF_VALIDATE_DEBUG_EVENT_END; ++event) - if (events & RTE_BIT64(event)) - rc = rc < 0 ? rc : debug_send_event(debug, event); + if (event == RTE_BPF_VALIDATE_DEBUG_EVENT_STEP) + rc = rc < 0 ? rc : debug_trigger_breakpoints(debug, pc); - if (events == 0 || events == RTE_BIT64( - RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_ENTER)) - /* Stepping into a real instruction to execute. */ - rc = rc < 0 ? rc : debug_points_call_back(debug, - &debug->breakpoint_lists[pc]); + rc = rc < 0 ? rc : debug_send_event(debug, event); + events -= RTE_BIT64(event); + } - rc = rc < 0 ? rc : debug_send_event(debug, - RTE_BPF_VALIDATE_DEBUG_EVENT_STEP); + if (events != 0) + /* Received unsupported events. */ + rc = rc < 0 ? rc : -EINVAL; return rc; } @@ -627,8 +651,6 @@ __rte_bpf_validate_debug_evaluate_finish(struct rte_bpf_validate_debug *debug, int result) { int rc = 0; - uint32_t pc; - debug_event_t event; if (debug == NULL) return 0; @@ -641,20 +663,10 @@ __rte_bpf_validate_debug_evaluate_finish(struct rte_bpf_validate_debug *debug, debug->evaluate_finished = true; debug->evaluate_result = result; - if (result != -ECANCELED) { - if (result < 0) { - /* Last known pc is the place we failed. */ - pc = debug->pc; - event = RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_FAILURE; - } else { - /* Show program end, not particular instruction. */ - pc = debug->bpf_prm->raw.nb_ins; - event = RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_SUCCESS; - } - - rc = __rte_bpf_validate_debug_evaluate_update(debug, pc, - RTE_BIT64(event)); - } + if (result != -ECANCELED) + rc = debug_send_event(debug, result < 0 ? + RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_FAILURE : + RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_SUCCESS); debug_evaluate_close(debug); diff --git a/lib/bpf/bpf_validate_debug.h b/lib/bpf/bpf_validate_debug.h index 2b0c3bb646a7..4603841db2ed 100644 --- a/lib/bpf/bpf_validate_debug.h +++ b/lib/bpf/bpf_validate_debug.h @@ -53,10 +53,9 @@ __rte_bpf_validate_debug_evaluate_start(struct rte_bpf_validate_debug *debug, * @param debug * Validate debug instance configured by user, can be NULL. * @param pc - * Current value of the program counter, or next after last instruction. + * Current value of the program counter. * @param events * Bitmask of events. - * Step bit is always implied and should not be set. * @return * Non-negative value: evaluation should continue; * -ECANCELED: evaluation should fail without calling this API again; diff --git a/lib/bpf/rte_bpf_validate_debug.h b/lib/bpf/rte_bpf_validate_debug.h index f30fa926f10a..b3fedd5428bc 100644 --- a/lib/bpf/rte_bpf_validate_debug.h +++ b/lib/bpf/rte_bpf_validate_debug.h @@ -31,25 +31,31 @@ extern "C" { * Supported validate events. * * Valid events begin from 0 and end before `RTE_BPF_VALIDATE_DEBUG_EVENT_END`. + * + * At any given evaluation step, callbacks are fired in the following order: + * - Validation start event; + * - Branching and invalid-state events (can be interleaved); + * - Instruction breakpoints (before evaluating instruction); + * - Step (before evaluating instruction) or validation result (if done) event; */ enum rte_bpf_validate_debug_event { - /* Just before every instruction, at branch or validation end. */ + /* Just before every evaluated instruction. */ RTE_BPF_VALIDATE_DEBUG_EVENT_STEP, /* Validator has failed its internal self-checks. */ RTE_BPF_VALIDATE_DEBUG_EVENT_INVALID_STATE, - /* Start of validation. */ + /* Start of validation (pc points to first instruction). */ RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_START, - /* Successful finish of validation. */ + /* Successful finish (pc undefined). */ RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_SUCCESS, - /* Finish of validation with error. */ + /* Finish of validation with error (pc points at error). */ RTE_BPF_VALIDATE_DEBUG_EVENT_VALIDATION_FAILURE, - /* Beginning of a branch just after the jump. */ + /* Beginning of a branch evaluation (pc points to branch start). */ RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_ENTER, - /* Pruning branch as verified earlier. */ + /* Pruning branch as verified earlier (pc points to branch start). */ RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_PRUNE, - /* End of branch verification, after the last verified instruction. */ + /* End of branch verification (pc points to jump instruction). */ RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_RETURN, - /* Pruning branch as dynamically unreachable. */ + /* Pruning branch as dynamically unreachable (pc points to branch start). */ RTE_BPF_VALIDATE_DEBUG_EVENT_BRANCH_UNREACHABLE, /* Number of valid event values. */ RTE_BPF_VALIDATE_DEBUG_EVENT_END, @@ -208,7 +214,7 @@ rte_bpf_validate_debug_get_last_point(const struct rte_bpf_validate_debug *debug * @param debug * Debug instance. * @return - * Current program counter being validated, or one after last. + * Current program counter being validated. * UINT32_MAX if no program is being validated. */ __rte_experimental -- 2.43.0

