Patch "bpf: Fix the corner case with may_goto and jump to the 1st insn." has been added to the 6.9-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    bpf: Fix the corner case with may_goto and jump to the 1st insn.

to the 6.9-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     bpf-fix-the-corner-case-with-may_goto-and-jump-to-th.patch
and it can be found in the queue-6.9 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 621cecbfe1991c23da45e062e914e38d727adef2
Author: Alexei Starovoitov <ast@xxxxxxxxxx>
Date:   Tue Jun 18 18:18:58 2024 -0700

    bpf: Fix the corner case with may_goto and jump to the 1st insn.
    
    [ Upstream commit 5337ac4c9b807bc46baa0713121a0afa8beacd70 ]
    
    When the following program is processed by the verifier:
    L1: may_goto L2
        goto L1
    L2: w0 = 0
        exit
    
    the may_goto insn is first converted to:
    L1: r11 = *(u64 *)(r10 -8)
        if r11 == 0x0 goto L2
        r11 -= 1
        *(u64 *)(r10 -8) = r11
        goto L1
    L2: w0 = 0
        exit
    
    then later as the last step the verifier inserts:
      *(u64 *)(r10 -8) = BPF_MAX_LOOPS
    as the first insn of the program to initialize loop count.
    
    When the first insn happens to be a branch target of some jmp the
    bpf_patch_insn_data() logic will produce:
    L1: *(u64 *)(r10 -8) = BPF_MAX_LOOPS
        r11 = *(u64 *)(r10 -8)
        if r11 == 0x0 goto L2
        r11 -= 1
        *(u64 *)(r10 -8) = r11
        goto L1
    L2: w0 = 0
        exit
    
    because instruction patching adjusts all jmps and calls, but for this
    particular corner case it's incorrect and the L1 label should be one
    instruction down, like:
        *(u64 *)(r10 -8) = BPF_MAX_LOOPS
    L1: r11 = *(u64 *)(r10 -8)
        if r11 == 0x0 goto L2
        r11 -= 1
        *(u64 *)(r10 -8) = r11
        goto L1
    L2: w0 = 0
        exit
    
    and that's what this patch is fixing.
    After bpf_patch_insn_data() call adjust_jmp_off() to adjust all jmps
    that point to newly insert BPF_ST insn to point to insn after.
    
    Note that bpf_patch_insn_data() cannot easily be changed to accommodate
    this logic, since jumps that point before or after a sequence of patched
    instructions have to be adjusted with the full length of the patch.
    
    Conceptually it's somewhat similar to "insert" of instructions between other
    instructions with weird semantics. Like "insert" before 1st insn would require
    adjustment of CALL insns to point to newly inserted 1st insn, but not an
    adjustment JMP insns that point to 1st, yet still adjusting JMP insns that
    cross over 1st insn (point to insn before or insn after), hence use simple
    adjust_jmp_off() logic to fix this corner case. Ideally bpf_patch_insn_data()
    would have an auxiliary info to say where 'the start of newly inserted patch
    is', but it would be too complex for backport.
    
    Fixes: 011832b97b31 ("bpf: Introduce may_goto instruction")
    Reported-by: Zac Ecob <zacecob@xxxxxxxxxxxxxx>
    Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx>
    Signed-off-by: Daniel Borkmann <daniel@xxxxxxxxxxxxx>
    Acked-by: Eduard Zingerman <eddyz87@xxxxxxxxx>
    Closes: https://lore.kernel.org/bpf/CAADnVQJ_WWx8w4b=6Gc2EpzAjgv+6A0ridnMz2TvS2egj4r3Gw@xxxxxxxxxxxxxx/
    Link: https://lore.kernel.org/bpf/20240619011859.79334-1-alexei.starovoitov@xxxxxxxxx
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index add5ccbe87523..2233bf50a9012 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12546,6 +12546,16 @@ static bool signed_add32_overflows(s32 a, s32 b)
 	return res < a;
 }
 
+static bool signed_add16_overflows(s16 a, s16 b)
+{
+	/* Do the add in u16, where overflow is well-defined */
+	s16 res = (s16)((u16)a + (u16)b);
+
+	if (b < 0)
+		return res > a;
+	return res < a;
+}
+
 static bool signed_sub_overflows(s64 a, s64 b)
 {
 	/* Do the sub in u64, where overflow is well-defined */
@@ -18564,6 +18574,39 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
 	return new_prog;
 }
 
+/*
+ * For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the
+ * jump offset by 'delta'.
+ */
+static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta)
+{
+	struct bpf_insn *insn = prog->insnsi;
+	u32 insn_cnt = prog->len, i;
+
+	for (i = 0; i < insn_cnt; i++, insn++) {
+		u8 code = insn->code;
+
+		if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) ||
+		    BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT)
+			continue;
+
+		if (insn->code == (BPF_JMP32 | BPF_JA)) {
+			if (i + 1 + insn->imm != tgt_idx)
+				continue;
+			if (signed_add32_overflows(insn->imm, delta))
+				return -ERANGE;
+			insn->imm += delta;
+		} else {
+			if (i + 1 + insn->off != tgt_idx)
+				continue;
+			if (signed_add16_overflows(insn->imm, delta))
+				return -ERANGE;
+			insn->off += delta;
+		}
+	}
+	return 0;
+}
+
 static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,
 					      u32 off, u32 cnt)
 {
@@ -20268,6 +20311,13 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
 		if (!new_prog)
 			return -ENOMEM;
 		env->prog = prog = new_prog;
+		/*
+		 * If may_goto is a first insn of a prog there could be a jmp
+		 * insn that points to it, hence adjust all such jmps to point
+		 * to insn after BPF_ST that inits may_goto count.
+		 * Adjustment will succeed because bpf_patch_insn_data() didn't fail.
+		 */
+		WARN_ON(adjust_jmp_off(env->prog, subprog_start, 1));
 	}
 
 	/* Since poke tab is now finalized, publish aux to tracker. */




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux