BPF List
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: Daniel Borkmann <daniel@iogearbox.net>, bpf@vger.kernel.org
Cc: nathaniel.theis@nccgroup.com, ast@kernel.org, andrii@kernel.org,
	 john.fastabend@gmail.com
Subject: Re: [PATCH bpf 1/3] bpf: Fix incorrect delta propagation between linked registers
Date: Wed, 16 Oct 2024 15:12:38 -0700	[thread overview]
Message-ID: <23c731fc96ef8c1d6f21983afa4aee593849e428.camel@gmail.com> (raw)
In-Reply-To: <20241016134913.32249-1-daniel@iogearbox.net>

On Wed, 2024-10-16 at 15:49 +0200, Daniel Borkmann wrote:
> Nathaniel reported a bug in the linked scalar delta tracking, which can lead
> to accepting a program with OOB access. The specific code is related to the
> sync_linked_regs() function and the BPF_ADD_CONST flag, which signifies a
> constant offset between two scalar registers tracked by the same register id.
> 
> The verifier attempts to track "similar" scalars in order to propagate bounds
> information learned about one scalar to others. For instance, if r1 and r2
> are known to contain the same value, then upon encountering 'if (r1 != 0x1234)
> goto xyz', not only does it know that r1 is equal to 0x1234 on the path where
> that conditional jump is not taken, it also knows that r2 is.
> 
> Additionally, with env->bpf_capable set, the verifier will track scalars
> which should be a constant delta apart (if r1 is known to be one greater than
> r2, then if r1 is known to be equal to 0x1234, r2 must be equal to 0x1233.)
> The code path for the latter in adjust_reg_min_max_vals() is reached when
> processing both 32 and 64-bit addition operations. While adjust_reg_min_max_vals()
> knows whether dst_reg was produced by a 32 or a 64-bit addition (based on the
> alu32 bool), the only information saved in dst_reg is the id of the source
> register (reg->id, or'ed by BPF_ADD_CONST) and the value of the constant
> offset (reg->off).
> 
> Later, the function sync_linked_regs() will attempt to use this information
> to propagate bounds information from one register (known_reg) to others,
> meaning, for all R in linked_regs, it copies known_reg range (and possibly
> adjusting delta) into R for the case of R->id == known_reg->id.
> 
> For the delta adjustment, meaning, matching reg->id with BPF_ADD_CONST, the
> verifier adjusts the register as reg = known_reg; reg += delta where delta
> is computed as (s32)reg->off - (s32)known_reg->off and placed as a scalar
> into a fake_reg to then simulate the addition of reg += fake_reg. This is
> only correct, however, if the value in reg was created by a 64-bit addition.
> When reg contains the result of a 32-bit addition operation, its upper 32
> bits will always be zero. sync_linked_regs() on the other hand, may cause
> the verifier to believe that the addition between fake_reg and reg overflows
> into those upper bits. For example, if reg was generated by adding the
> constant 1 to known_reg using a 32-bit alu operation, then reg->off is 1
> and known_reg->off is 0. If known_reg is known to be the constant 0xFFFFFFFF,
> sync_linked_regs() will tell the verifier that reg is equal to the constant
> 0x100000000. This is incorrect as the actual value of reg will be 0, as the
> 32-bit addition will wrap around.
> 
> Example:
> 
>   0: (b7) r0 = 0;             R0_w=0
>   1: (18) r1 = 0x80000001;    R1_w=0x80000001
>   3: (37) r1 /= 1;            R1_w=scalar()
>   4: (bf) r2 = r1;            R1_w=scalar(id=1) R2_w=scalar(id=1)
>   5: (bf) r4 = r1;            R1_w=scalar(id=1) R4_w=scalar(id=1)
>   6: (04) w2 += 2147483647;   R2_w=scalar(id=1+2147483647,smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
>   7: (04) w4 += 0 ;           R4_w=scalar(id=1+0,smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
>   8: (15) if r2 == 0x0 goto pc+1
>  10: R0=0 R1=0xffffffff80000001 R2=0x7fffffff R4=0xffffffff80000001 R10=fp0
> 
> What can be seen here is that r1 is copied to r2 and r4, such that {r1,r2,r4}.id
> are all the same which later lets sync_linked_regs() to be invoked. Then, in
> a next step constants are added with alu32 to r2 and r4, setting their ->off,
> as well as id |= BPF_ADD_CONST. Next, the conditional will bind r2 and
> propagate ranges to its linked registers. The verifier now believes the upper
> 32 bits of r4 are r4=0xffffffff80000001, while actually r4=r1=0x80000001.
> 
> One approach for a simple fix suitable also for stable is to limit the constant
> delta tracking to only 64-bit alu addition. If necessary at some later point,
> BPF_ADD_CONST could be split into BPF_ADD_CONST64 and BPF_ADD_CONST32 to avoid
> mixing the two under the tradeoff to further complicate sync_linked_regs().
> However, none of the added tests from dedf56d775c0 ("selftests/bpf: Add tests
> for add_const") make this necessary at this point, meaning, BPF CI also passes
> with just limiting tracking to 64-bit alu addition.
> 
> Fixes: 98d7ca374ba4 ("bpf: Track delta between "linked" registers.")
> Reported-by: Nathaniel Theis <nathaniel.theis@nccgroup.com>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---

Thank you for the fix, missed this on the code review :(

Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>

[...]


  parent reply	other threads:[~2024-10-16 22:12 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-16 13:49 [PATCH bpf 1/3] bpf: Fix incorrect delta propagation between linked registers Daniel Borkmann
2024-10-16 13:49 ` [PATCH bpf 2/3] bpf: Fix print_reg_state's constant scalar dump Daniel Borkmann
2024-10-16 20:16   ` Andrii Nakryiko
2024-10-16 13:49 ` [PATCH bpf 3/3] selftests/bpf: Add test case for delta propagation Daniel Borkmann
2024-10-16 21:56   ` Eduard Zingerman
2024-10-16 22:12 ` Eduard Zingerman [this message]
2024-10-17 18:10 ` [PATCH bpf 1/3] bpf: Fix incorrect delta propagation between linked registers patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=23c731fc96ef8c1d6f21983afa4aee593849e428.camel@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=john.fastabend@gmail.com \
    --cc=nathaniel.theis@nccgroup.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox