bpf/verifier: when pruning a branch, ignore its write marks
authorEdward Cree <ecree@solarflare.com>
Wed, 23 Aug 2017 14:10:03 +0000 (15:10 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Aug 2017 05:38:07 +0000 (22:38 -0700)
The fact that writes occurred in reaching the continuation state does
 not screen off its reads from us, because we're not really its parent.
So detect 'not really the parent' in do_propagate_liveness, and ignore
 write marks in that case.

Fixes: dc503a8ad984 ("bpf/verifier: track liveness for pruning")
Signed-off-by: Edward Cree <ecree@solarflare.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
kernel/bpf/verifier.c

index e42c096ba20d0edbce81920b9744d48445687829..fdbaa608655936fd9e67cdbaba915dc6b607c246 100644 (file)
@@ -3436,6 +3436,7 @@ out_free:
 static bool do_propagate_liveness(const struct bpf_verifier_state *state,
                                  struct bpf_verifier_state *parent)
 {
+       bool writes = parent == state->parent; /* Observe write marks */
        bool touched = false; /* any changes made? */
        int i;
 
@@ -3447,7 +3448,9 @@ static bool do_propagate_liveness(const struct bpf_verifier_state *state,
        for (i = 0; i < BPF_REG_FP; i++) {
                if (parent->regs[i].live & REG_LIVE_READ)
                        continue;
-               if (state->regs[i].live == REG_LIVE_READ) {
+               if (writes && (state->regs[i].live & REG_LIVE_WRITTEN))
+                       continue;
+               if (state->regs[i].live & REG_LIVE_READ) {
                        parent->regs[i].live |= REG_LIVE_READ;
                        touched = true;
                }
@@ -3460,7 +3463,9 @@ static bool do_propagate_liveness(const struct bpf_verifier_state *state,
                        continue;
                if (parent->spilled_regs[i].live & REG_LIVE_READ)
                        continue;
-               if (state->spilled_regs[i].live == REG_LIVE_READ) {
+               if (writes && (state->spilled_regs[i].live & REG_LIVE_WRITTEN))
+                       continue;
+               if (state->spilled_regs[i].live & REG_LIVE_READ) {
                        parent->spilled_regs[i].live |= REG_LIVE_READ;
                        touched = true;
                }