Received: by 2002:a05:7412:b995:b0:f9:9502:5bb8 with SMTP id it21csp7961459rdb; Thu, 4 Jan 2024 13:30:24 -0800 (PST) X-Google-Smtp-Source: AGHT+IGpznbJocPC7Li9DRMeKcpDcmUzyE0qOVr0eZ1PnfyJ4F8gaKvZtaaP0cMNFmlsmio9Jk7J X-Received: by 2002:a5d:9058:0:b0:7bb:d936:b33 with SMTP id v24-20020a5d9058000000b007bbd9360b33mr1412520ioq.28.1704403824036; Thu, 04 Jan 2024 13:30:24 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1704403824; cv=none; d=google.com; s=arc-20160816; b=MUtIVog2CxN59zhGhbO5VTonKgEzstCFBxB9ADsanpe40f0ufXRCHTDS9dI5ZZ+P0v ETlUia8Htiw6uhs4I8ydd304NnczAjC7fDN1aBVkHC/3hNfxf9BTx8vT/iJVNc20YvZV aBqiNPF0PxIiN4shhhCvrwpcuGBFNGdNkfRS5hKv9+yVvirLDYylWGILcTaPVOw1pFR2 KRZ2l0hfCYkU0nnrmb0oQBm77tB4Pce9YP4oZmu+6em/TNopdyFq14Oi3V8AZlnBVq15 wW8P716mc6e4MHzIHv5JNN1mO+3SOgh7ye4D3/f/kEXFJTYK2NiUVqueXTdicyuPKbHO 7taQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:date:message-id:dkim-signature; bh=mOzrK40wE2i86/1eoThEmcjck6rrrkGEQCO3495UBJg=; fh=PcGKppcj9GAcIjaNcGS83sbr5kRkrVW+fhLN+HJQakE=; b=abAtSvT/WrF6UMJSl2SYuzvJHqs0ZzOawDg1cMuUWpEvolqpejmwRz3E1Yp/Jcf7wv xfw9+66IKxIbXt9qCqjoVzindaF6Vr9cIsC0ke1S4hbS8OLPaowospkJBgjla8oEIw8R FFjgwLyJnHOLwRzjfQUffAm8wbWWbII53+9bl9b6GrPUfRjpyCRjjvykIh4O16X3YOOD 7Dhlb4HH6+EhrhAjFdhmM77ON7qg+gHllnmT8SD+qefdLVmXKeRHiPqy5PIfymJ+Ljp9 D0eyMPtB2lHRF36Ja/CRX3olM8vDTth+QYi79dqEqYVyfnDnqWQE+mgxz0PeNwLW8SI3 SqwA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=4X7V4v9X; spf=pass (google.com: domain of linux-kernel+bounces-17240-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-17240-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [2604:1380:40f1:3f00::1]) by mx.google.com with ESMTPS id x23-20020a63b217000000b005ce037d6bd4si204313pge.96.2024.01.04.13.30.23 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Jan 2024 13:30:24 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-17240-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) client-ip=2604:1380:40f1:3f00::1; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=4X7V4v9X; spf=pass (google.com: domain of linux-kernel+bounces-17240-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:40f1:3f00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-17240-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id EE67BB22A0D for ; Thu, 4 Jan 2024 21:30:21 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 16A4C2C843; Thu, 4 Jan 2024 21:30:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="4X7V4v9X" X-Original-To: linux-kernel@vger.kernel.org Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C3642C6B0 for ; Thu, 4 Jan 2024 21:30:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=google.com Received: by mail-qk1-f180.google.com with SMTP id af79cd13be357-781bbb1f61fso57903785a.3 for ; Thu, 04 Jan 2024 13:30:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1704403809; x=1705008609; darn=vger.kernel.org; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=mOzrK40wE2i86/1eoThEmcjck6rrrkGEQCO3495UBJg=; b=4X7V4v9XYwOdZeFfYanur4C4xqOB53IsiSTiamVqTSSIm1eD06zVmF9j8rumCw4/75 uGfr7LN7l3+vBRRTg9cjbj24vxfPCJZB7gfsrESPfxOEsotO3TLFWbeEpyUwaCieclib +akm7EHN3COoEVDrBoi/OLQNvMg+WuEvvodl2pIVid3xnnTJYIUOL7GbFZnk1nDYuofJ kWhwBDkmnhh9IrTVvhpYM1E7iaMov0gKm8iyw0DNbWsF13evwMYO4+QID6vh+iKLhST3 ZVge1fazaL5eKVoAl6upZ3q3XONu3ZP49dYqcmT2oOUp/lxt1kyLm2rYurRiEEaapHn4 Xo9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704403809; x=1705008609; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=mOzrK40wE2i86/1eoThEmcjck6rrrkGEQCO3495UBJg=; b=xFHo9uuYFZFzLDFfKjfj8tiG/4h3LzJ6OjCZBHzauLTgZm2Kq6q8iX+czl/M1vdLxQ EvPCX7slOzWUemhdHca+cOCf1is9Qd+5jHCI3RLr5tHCHDC/daMDDrSnn3+97gjlJC3U 3V5BC/wMszv4DpgKZOcIb2TFr4P4Axi8vUkSRTePkKeomcIJwXIzY45Lm3tnBybL5Fk0 b44IXGZtvCvoUc45H9JIovyOzGthoWb3FC9hWNfxwEX05L+j71NbYkSr40PxfOE4pcSz D+yW9xrBH2ynjJMAihqDjDnLoBuh5hRnUjsfY7wU2s3RFRyk/sx/GHArBziFxMgxWAVI 0LIg== X-Gm-Message-State: AOJu0Yz+V70Aa8Q+TXnrJsjrzv28ErKIPGwuvUK3fE91mPA2zdigEea3 SmTuh048a6nqs4FPNHjEzvqk609ab4QK X-Received: by 2002:a05:620a:4895:b0:781:5db7:e1d3 with SMTP id ea21-20020a05620a489500b007815db7e1d3mr1394394qkb.108.1704403809002; Thu, 04 Jan 2024 13:30:09 -0800 (PST) Received: from [192.168.1.31] (d-65-175-157-166.nh.cpe.atlanticbb.net. [65.175.157.166]) by smtp.gmail.com with ESMTPSA id y20-20020a37e314000000b0077fb4ae1a71sm96785qki.93.2024.01.04.13.30.08 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 04 Jan 2024 13:30:08 -0800 (PST) Message-ID: Date: Thu, 4 Jan 2024 16:30:06 -0500 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 bpf-next 2/2] selftests/bpf: add inline assembly helpers to access array elements Content-Language: en-US To: Yonghong Song Cc: Jiri Olsa , Eddy Z , Andrii Nakryiko , Alexei Starovoitov , Daniel Borkmann , Song Liu , mattbobrowski@google.com, bpf@vger.kernel.org, linux-kernel@vger.kernel.org References: <20240103185403.610641-1-brho@google.com> <20240103185403.610641-3-brho@google.com> From: Barret Rhoden In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 1/4/24 12:31, Yonghong Song wrote: [snip] >>> +/* >>> + * Access an array element within a bound, such that the verifier >>> knows the >>> + * access is safe. >>> + * >>> + * This macro asm is the equivalent of: >>> + * >>> + *    if (!arr) >>> + *        return NULL; >>> + *    if (idx >= arr_sz) >>> + *        return NULL; >>> + *    return &arr[idx]; >>> + * >>> + * The index (___idx below) needs to be a u64, at least for certain >>> versions of >>> + * the BPF ISA, since there aren't u32 conditional jumps. >>> + */ >>> +#define bpf_array_elem(arr, arr_sz, idx) ({                \ >>> +    typeof(&(arr)[0]) ___arr = arr;                    \ >>> +    __u64 ___idx = idx;                        \ >>> +    if (___arr) {                            \ >>> +        asm volatile("if %[__idx] >= %[__bound] goto 1f;    \ >>> +                  %[__idx] *= %[__size];        \ >>> +                  %[__arr] += %[__idx];        \ >>> +                  goto 2f;                \ >>> +                  1:;                \ >>> +                  %[__arr] = 0;            \ >>> +                  2:                \ >>> +                  "                        \ >>> +                 : [__arr]"+r"(___arr), [__idx]"+r"(___idx)    \ >>> +                 : [__bound]"r"((arr_sz)),                \ >>> +                   [__size]"i"(sizeof(typeof((arr)[0])))    \ >>> +                 : "cc");                    \ >>> +    }                                \ >>> +    ___arr;                                \ >>> +}) > > The LLVM bpf backend has made some improvement to handle the case like >   r1 = ... >   r2 = r1 + 1 >   if (r2 < num) ... >   using r1 > by preventing generating the above code pattern. > > The implementation is a pattern matching style so surely it won't be > able to cover all cases. > > Do you have specific examples which has verification failure due to > false array out of bound access? Not in a small example. =( This bug has an example, but it was part of a larger program: https://github.com/google/ghost-userspace/issues/31 The rough progression was: - sometimes the compiler optimizes out the checks. So we added a macro to make the compiler not know the value of the variable anymore. - then, the compiler would occasionally do the check on a copy of the register, so we did the comparison and index operation all in assembly. I tried using bpf_cmp_likely() in my actual program (not just a one-off test), and still had a verifier issue. It's a large and convoluted program, so it might be hard to get a small reproducer. But it a different compiler issue than the one you mentioned. Specifically, I swapped out my array-access-macro for this one, using bpf_cmp_likely(): #define bpf_array_elem(arr, arr_sz, idx) ({ \ typeof(&(arr)[0]) ___arr = arr; \ typeof(&(arr)[0]) ___ret = 0; \ u64 ___idx = idx; \ if (___arr && bpf_cmp_likely(___idx, <, arr_sz)) \ ___ret = &___arr[___idx];\ ___ret; \ }) which should be the same logic as before: * if (!arr) * return NULL; * if (idx >= arr_sz) * return NULL; * return &arr[idx]; The issue I run into is different than the one you had. The compiler did the bounds check, but then for some reason recreated the index. The index is coming from another map. Arguably, the verifier is doing its job - that value could have changed. I just don't want the compiler to do the reread or any other shenanigans in between the bounds check and the usage. The guts of the error: - r0 is the map (L127) - r1 is the index, read from another map (L128) - r1 gets verified to be less than 0x200 (L129) - some other stuff happens - r1 gets read again, and is no longer bound (L132) - r1 gets scaled up by 896. (896*0x200 = 0x70000, would be the real bound, but r1 lost the 0x200 bound) - r0 indexed by the bad r1 (L134) - blow up (L143) 127: (15) if r0 == 0x0 goto pc+1218 ; R0=map_value(off=0,ks=4,vs=458752,imm=0) 128: (79) r1 = *(u64 *)(r10 -40) ; R1_w=Pscalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R10=fp0 129: (35) if r1 >= 0x200 goto pc+1216 ; R1_w=Pscalar(umax=511,var_off=(0x0; 0x1ff)) 130: (79) r4 = *(u64 *)(r10 -56) ; R4_w=Pscalar() R10=fp0; 131: (37) r4 /= 1000 ; R4_w=Pscalar() 132: (79) r1 = *(u64 *)(r10 -40) ; R1_w=Pscalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R10=fp0; 133: (27) r1 *= 896 ; R1_w=Pscalar(umax=3848290696320,var_off=(0x0; 0x3ffffffff80),s32_max=2147483520,u32_max=-128) 134: (0f) r0 += r1 ; R0_w=map_value(off=0,ks=4,vs=458752,umax=3848290696320,var_off=(0x0; 0x3ffffffff80),s32_max=2147483520,u32_max=-128) R1_w=Pscalar(umax=3848290696320,var_off=(0x0; 0x3ffffffff80),s32_max=2147483520,u32_max=-128) 135: (79) r3 = *(u64 *)(r10 -48) ; R3_w=map_value(off=0,ks=4,vs=15728640,imm=0) R10=fp0; 136: (0f) r3 += r8 ; R3_w=map_value(off=0,ks=4,vs=15728640,umax=15728400,var_off=(0x0; 0xfffff0),s32_max=16777200,u32_max=16777200) R8=Pscalar(umax=15728400,var_off=(0x0; 0xfffff0)) 137: (61) r1 = *(u32 *)(r7 +16) ; R1_w=Pscalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R7=map_value(id=18779,off=0,ks=4,vs=224,imm=0) 138: (79) r2 = *(u64 *)(r3 +88) ; R2=Pscalar() R3=map_value(off=0,ks=4,vs=15728640,umax=15728400,var_off=(0x0; 0xfffff0),s32_max=16777200,u32_max=16777200) 139: (a5) if r1 < 0x9 goto pc+1 ; R1=Pscalar(umin=9,umax=4294967295,var_off=(0x0; 0xffffffff)) 140: (b7) r1 = 0 ; R1_w=P0 141: (27) r1 *= 72 ; R1_w=P0 142: (0f) r0 += r1 ; R0_w=map_value(off=0,ks=4,vs=458752,umax=3848290696320,var_off=(0x0; 0x3ffffffff80),s32_max=2147483520,u32_max=-128) R1_w=P0 143: (7b) *(u64 *)(r0 +152) = r2 if i put in a little ASM magic to tell the compiler to not recreate the index, it works, like so: #define BPF_MUST_CHECK(x) ({ asm volatile ("" : "+r"(x)); x; }) #define bpf_array_elem(arr, arr_sz, idx) ({ \ typeof(&(arr)[0]) ___arr = arr; \ typeof(&(arr)[0]) ___ret = 0; \ u64 ___idx = idx; \ BPF_MUST_CHECK(___idx); \ if (___arr && bpf_cmp_likely(___idx, <, arr_sz)) \ ___ret = &___arr[___idx];\ ___ret; \ }) though anecdotally, that only stops the "reread the index from its map" problem, similar to a READ_ONCE. the compiler is still free to just use another register for the check. The bit of ASM i had from a while back that did that was: * r2 = r8 * r2 <<= 32 * r2 >>= 32 * if r2 > 0x3ff goto pc+29 * r8 <<= 32 * r8 >>= 32 * r8 <<= 6 * r0 += r8 * *(u64 *)(r0 +48) = r3 where r2 was bounds checked, but r8 was used instead. I'll play around and see if I can come up with a selftest that can run into any of these "you did the check, but threw the check away" scenarios. Thanks, Barret