From: Yonghong Song <yonghong.song@linux.dev>
To: Daniel Borkmann <daniel@iogearbox.net>, bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
kernel-team@fb.com, Martin KaFai Lau <martin.lau@kernel.org>,
Zac Ecob <zacecob@protonmail.com>,
dthaler1968@googlemail.com
Subject: Re: [PATCH bpf-next 1/2] bpf: Fix a sdiv overflow issue
Date: Wed, 11 Sep 2024 08:14:50 -0700 [thread overview]
Message-ID: <1013fea7-b913-480c-a642-b8aaa71e3ac1@linux.dev> (raw)
In-Reply-To: <bf721309-0bf7-667c-16c9-b2601e033fe7@iogearbox.net>
On 9/11/24 7:18 AM, Daniel Borkmann wrote:
> On 9/11/24 6:40 AM, Yonghong Song wrote:
>> Zac Ecob reported a problem where a bpf program may cause kernel
>> crash due
>> to the following error:
>> Oops: divide error: 0000 [#1] PREEMPT SMP KASAN PTI
>>
>> The failure is due to the below signed divide:
>> LLONG_MIN/-1 where LLONG_MIN equals to -9,223,372,036,854,775,808.
>> LLONG_MIN/-1 is supposed to give a positive number
>> 9,223,372,036,854,775,808,
>> but it is impossible since for 64-bit system, the maximum positive
>> number is 9,223,372,036,854,775,807. On x86_64, LLONG_MIN/-1 will
>> cause a kernel exception. On arm64, the result for LLONG_MIN/-1 is
>> LLONG_MIN.
>>
>> So for 64-bit signed divide (sdiv), some additional insns are patched
>> to check LLONG_MIN/-1 pattern. If such a pattern does exist, the result
>> will be LLONG_MIN. Otherwise, it follows normal sdiv operation.
>
> I presume this could be follow-up but it would also need an update to [0]
> to describe the behavior.
>
> [0] Documentation/bpf/standardization/instruction-set.rst
I will do this as a follow-up. Will cover all cases including this patch
plus existing patched insn to handle r1/r2 and r1%r2 where runtime check r2
could be 0.
>
>> [1]
>> https://lore.kernel.org/bpf/tPJLTEh7S_DxFEqAI2Ji5MBSoZVg7_G-Py2iaZpAaWtM961fFTWtsnlzwvTbzBzaUzwQAoNATXKUlt0LZOFgnDcIyKCswAnAGdUF3LBrhGQ=@protonmail.com/
>>
>> Reported-by: Zac Ecob <zacecob@protonmail.com>
>> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
>> ---
>> kernel/bpf/verifier.c | 29 ++++++++++++++++++++++++++---
>> 1 file changed, 26 insertions(+), 3 deletions(-)
>>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index f35b80c16cda..d77f1a05a065 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -20506,6 +20506,7 @@ static int do_misc_fixups(struct
>> bpf_verifier_env *env)
>> insn->code == (BPF_ALU | BPF_DIV | BPF_X)) {
>> bool is64 = BPF_CLASS(insn->code) == BPF_ALU64;
>> bool isdiv = BPF_OP(insn->code) == BPF_DIV;
>> + bool is_sdiv64 = is64 && isdiv && insn->off == 1;
>> struct bpf_insn *patchlet;
>> struct bpf_insn chk_and_div[] = {
>> /* [R,W]x div 0 -> 0 */
>> @@ -20525,10 +20526,32 @@ static int do_misc_fixups(struct
>> bpf_verifier_env *env)
>> BPF_JMP_IMM(BPF_JA, 0, 0, 1),
>> BPF_MOV32_REG(insn->dst_reg, insn->dst_reg),
>> };
>> + struct bpf_insn chk_and_sdiv64[] = {
>> + /* Rx sdiv 0 -> 0 */
>> + BPF_RAW_INSN(BPF_JMP | BPF_JNE | BPF_K, insn->src_reg,
>> + 0, 2, 0),
>> + BPF_ALU32_REG(BPF_XOR, insn->dst_reg, insn->dst_reg),
>> + BPF_JMP_IMM(BPF_JA, 0, 0, 8),
>> + /* LLONG_MIN sdiv -1 -> LLONG_MIN */
>> + BPF_RAW_INSN(BPF_JMP | BPF_JNE | BPF_K, insn->src_reg,
>> + 0, 6, -1),
>> + BPF_LD_IMM64(insn->src_reg, LLONG_MIN),
>> + BPF_RAW_INSN(BPF_JMP | BPF_JNE | BPF_X, insn->dst_reg,
>> + insn->src_reg, 2, 0),
>> + BPF_MOV64_IMM(insn->src_reg, -1),
>> + BPF_JMP_IMM(BPF_JA, 0, 0, 2),
>> + BPF_MOV64_IMM(insn->src_reg, -1),
>
> Looks good, we could probably shrink this snippet via BPF_REG_AX ?
> Untested, like below:
>
> + /* Rx sdiv 0 -> 0 */
> + BPF_RAW_INSN(BPF_JMP | BPF_JNE | BPF_K,
> insn->src_reg, 0, 2, 0),
> + BPF_ALU32_REG(BPF_XOR, insn->dst_reg, insn->dst_reg),
> + BPF_JMP_IMM(BPF_JA, 0, 0, 5),
> + /* LLONG_MIN sdiv -1 -> LLONG_MIN */
> + BPF_RAW_INSN(BPF_JMP | BPF_JNE | BPF_K,
> insn->src_reg, 0, 2, -1),
> + BPF_LD_IMM64(BPF_REG_AX, LLONG_MIN),
> + BPF_RAW_INSN(BPF_JMP | BPF_JEQ | BPF_X,
> insn->dst_reg, BPF_REG_AX, 1, 0),
> + *insn,
>
> Then we don't need to restore the src_reg in both paths.
Indeed, this is much simpler. I forgot to use BPF_REG_AX somehow...
>
>> + *insn,
>> + };
>
> Have you also looked into rejecting this pattern upfront on load when
> its a known
> constant as we do with div by 0 in check_alu_op()?
We probably cannot do this for this sdiv case. For example,
r1/0 or r1%0 can be rejected by verifier.
But r1/-1 cannot be rejected as most likely r1 is not a constant LLONG_MIN.
But if the divisor is constant -1, we can patch insn to handle case r1/-1.
>
> Otherwise lgtm if this is equivalent to arm64 as you describe.
>
>> - patchlet = isdiv ? chk_and_div : chk_and_mod;
>> - cnt = isdiv ? ARRAY_SIZE(chk_and_div) :
>> - ARRAY_SIZE(chk_and_mod) - (is64 ? 2 : 0);
>> + if (is_sdiv64) {
>> + patchlet = chk_and_sdiv64;
>> + cnt = ARRAY_SIZE(chk_and_sdiv64);
>> + } else {
>> + patchlet = isdiv ? chk_and_div : chk_and_mod;
>> + cnt = isdiv ? ARRAY_SIZE(chk_and_div) :
>> + ARRAY_SIZE(chk_and_mod) - (is64 ? 2 : 0);
>> + }
>> new_prog = bpf_patch_insn_data(env, i + delta,
>> patchlet, cnt);
>> if (!new_prog)
>>
>
next prev parent reply other threads:[~2024-09-11 15:15 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-11 4:40 [PATCH bpf-next 1/2] bpf: Fix a sdiv overflow issue Yonghong Song
2024-09-11 4:40 ` [PATCH bpf-next 2/2] selftests/bpf: Add a couple of tests for potential sdiv overflow Yonghong Song
2024-09-11 14:18 ` [PATCH bpf-next 1/2] bpf: Fix a sdiv overflow issue Daniel Borkmann
2024-09-11 15:14 ` Yonghong Song [this message]
2024-09-11 15:52 ` Alexei Starovoitov
2024-09-11 17:01 ` Yonghong Song
2024-09-11 17:17 ` Andrii Nakryiko
2024-09-11 17:32 ` Yonghong Song
2024-09-12 6:54 ` kernel test robot
2024-09-12 16:43 ` Yonghong Song
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=1013fea7-b913-480c-a642-b8aaa71e3ac1@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=dthaler1968@googlemail.com \
--cc=kernel-team@fb.com \
--cc=martin.lau@kernel.org \
--cc=zacecob@protonmail.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.