Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpf: fix control-flow graph checking in privileged mode
When BPF program is verified in privileged mode, BPF verifier allows bounded loops. This means that from CFG point of view there are definitely some back-edges. Original commit adjusted check_cfg() logic to not detect back-edges in control flow graph if they are resulting from conditional jumps, which the idea that subsequent full BPF verification process will determine whether such loops are bounded or not, and either accept or reject the BPF program. At least that's my reading of the intent. Unfortunately, the implementation of this idea doesn't work correctly in all possible situations. Conditional jump might not result in immediate back-edge, but just a few unconditional instructions later we can arrive at back-edge. In such situations check_cfg() would reject BPF program even in privileged mode, despite it might be bounded loop. Next patch adds one simple program demonstrating such scenario. So, this patch fixes this limitation by tracking not just immediate conditional jump, but also all subsequent instructions that happened in such conditional branch. For that we store a new flag, CONDITIONAL, along with current DISCOVERED, EXPLORED, BRANCH, and FALLTHROUGH. Conditional jump instructions forces CONDITIONAL flag, and in all other situations we "inherit" this flag based on whether we arrived at given instruction with CONDITIONAL flag during discovery step. Note, this approach doesn't detect some obvious infinite loops during check_cfg() if they happen inside conditional code path. This can be fixed with more sophisticated DFS state implementation, where we'd remember some sort of "conditional epoch", and so if a sequence of jumps or sequential instructions lead to back-edge within the same epoch, that a loop within the same branch. But I didn't add that for two reasons. First, subsequent BPF verifier logic will detect this and prevent anyways, and it's easy to do the same with just conditional jumps, so there isn't much of a difference in supporting this. But also, second, this conditional jump branch might never be taken and thus will be a dead code. And it seems desirable to be able to express "this shall not be executed, otherwise we'll while(true){}" logic as a kind of unreachable guard. So keeping things simple and allowing this dead code elimination approach to work. Note a few test changes. For unknown reason, we have a few tests that are specified to detect a back-edge in a privileged mode, but looking at their code it seems like the right outcome is passing check_cfg() and letting subsequent verification to make a decision about bounded or not bounded looping. Bounded recursion case is also interesting. The example should pass, as recursion is limited to just a few levels and so we never reach maximum number of nested frames and never exhaust maximum stack depth. But the way that max stack depth logic works today it falsely detects this as exceeding max nested frame count. This patch series doesn't attempt to fix this orthogonal problem, so we just adjust expected verifier failure. Fixes: 2589726 ("bpf: introduce bounded loops") Reported-by: Hao Sun <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]>
- Loading branch information