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

Reply via email to