Skip to content

Conversation

theihor
Copy link
Collaborator

@theihor theihor commented Aug 29, 2025

So far dependency tracking in bpfvv relied only on interpretation of individual instructions. For example, r1 = r6 is parsed as ALU instruction that reads r6 and writes to r1.

BPF verifier often prints relevant values for instructions, and bpfvv of course parses them to build an array of BpfState objects.

This change expands the dependency tracking to include information about value changes, as reported by the verifier.

For example:

26: (bf) r6 = r0 ; R0_w=map_value_or_null(id=1,map=bpfj_pod_map,ks=4,vs=1040) R6_w=map_value_or_null(id=1,map=bpfj_pod_map,ks=4,vs=1040)
...
29: (15) if r6 == 0x0 goto pc+27 ; R6=map_value(map=bpfj_pod_map,ks=4,vs=1040)

Note that the value of r6 has changed from map_value_or_null to map_value, even though there was no actual writes to r6 in the instruction stream. It is correct however, because in this trace verifier is exploring a branch where r6 is not equal to 0, and so it's value (as interpreted by the verifier) did actually change.

In bpfvv, we can notice such value changes and take them into account when calculating dependencies. This also has an additional benefit of indirect stack access tracking, at least in simple cases, such as:

525: (bf) r1 = r10                    ; frame2: R1_w=fp0 R10=fp0
526: (07) r1 += -24                   ; frame2: R1_w=fp-24
...
529: (7b) *(u64 *)(r1 +0) = r8        ; frame2: R1_w=fp-24 R8=scalar(id=102) fp-24_w=scalar(id=102)
...
999: (79) r6 = *(u64 *)(r10 -24) ;

With this change, the user can now see the dependency of instruction at 999 on 529, even though stack access is indirect.

@theihor theihor requested a review from jordalgo August 29, 2025 21:49
So far dependency tracking in bpfvv relied only on interpretation of
individual instructions. For example, `r1 = r6` is parsed as ALU
instruction that *reads* r6 and *writes* to r1.

BPF verifier often prints relevant values for instructions, and bpfvv
of course parses them to build an array of BpfState objects.

This change expands the dependency tracking to include information
about value changes, as reported by the verifier.

For example:

    26: (bf) r6 = r0 ; R0_w=map_value_or_null(id=1,map=bpfj_pod_map,ks=4,vs=1040) R6_w=map_value_or_null(id=1,map=bpfj_pod_map,ks=4,vs=1040)
    ...
    29: (15) if r6 == 0x0 goto pc+27 ; R6=map_value(map=bpfj_pod_map,ks=4,vs=1040)

Note that the value of r6 has changed from `map_value_or_null` to
`map_value`, even though there was no actual writes to r6 in the
instruction stream. It is correct however, because in this trace
verifier is exploring a branch where r6 is not equal to 0, and so it's
value (as interpreted by the verifier) did actually change.

In bpfvv, we can notice such value changes and take them into account
when calculating dependencies. This also has an additional benefit of
indirect stack access tracking, at least in simple cases, such as:

    525: (bf) r1 = r10                    ; frame2: R1_w=fp0 R10=fp0
    526: (07) r1 += -24                   ; frame2: R1_w=fp-24
    ...
    529: (7b) *(u64 *)(r1 +0) = r8        ; frame2: R1_w=fp-24 R8=scalar(id=102) fp-24_w=scalar(id=102)
    ...
    999: (79) r6 = *(u64 *)(r10 -24) ;

With this change, the user can now see the dependency of instruction
at 999 on 529, even though stack access is indirect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants