On 7/8/20 12:10 PM, Nick Desaulniers wrote: > On Tue, Jul 7, 2020 at 3:43 PM Alex Elder <elder@xxxxxxxxxx> wrote: >> >> On 7/7/20 4:16 PM, Nick Desaulniers wrote: >>> From: Jakub Kicinski <kuba@xxxxxxxxxx> >>> >>> When ur_load_imm_any() is inlined into jeq_imm(), it's possible for the >>> compiler to deduce a case where _val can only have the value of -1 at >>> compile time. Specifically, >>> >>> /* struct bpf_insn: _s32 imm */ >>> u64 imm = insn->imm; /* sign extend */ >>> if (imm >> 32) { /* non-zero only if insn->imm is negative */ >>> /* inlined from ur_load_imm_any */ >>> u32 __imm = imm >> 32; /* therefore, always 0xffffffff */ >>> if (__builtin_constant_p(__imm) && __imm > 255) >>> compiletime_assert_XXX() >>> >>> This can result in tripping a BUILD_BUG_ON() in __BF_FIELD_CHECK() that >>> checks that a given value is representable in one byte (interpreted as >>> unsigned). > > Hi Alex, > Thanks for taking a look. They're good and fair questions. > >> >> Why does FIELD_FIT() pass an unsigned long long value as the second >> argument to __BF_FIELD_CHECK()? > > Was Jakub's suggestion; I don't feel strongly against it either way, though... > >> Could it pass (typeof(_mask))0 instead? > > ...might be nice to avoid implicit promotions and conversions if _mask > is not the same sizeof _val. > >> It wouldn't fix this particular case, because UR_REG_IMM_MAX is also >> defined with that type. But (without working through this in more >> detail) it seems like there might be a solution that preserves the >> compile-time checking. > > I'd argue the point of the patch is to not check at compile time for > FIELD_FIT, since we have a case in > drivers/net/ethernet/netronome/nfp/bpf/jit.c (jeq_imm()) that will > always pass -1 (unintentionally due to compiler optimization). I understand why something needs to be done to handle that case. There's fancy macro gymnastics in "bitfield.h" to add convenient build-time checks for usage problems; I just thought there might be something we could do to preserve the checking--even in this case. But figuring that out takes more time than I was willing to spend on it yesterday... >> A second comment about this is that it might be nice to break >> __BF_FIELD_CHECK() into the parts that verify the mask (which >> could be used by FIELD_FIT() here) and the parts that verify >> other things. > > Like so? Jakub, WDYT? Or do you prefer v1+Alex's suggestion about > using `(typeof(_mask))0` in place of 0ULL? Yes, very much like that! But you could do that as a follow-on instead, so as not to delay or confuse things. Thanks. -Alex > diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c > b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c > index 311a5be25acb..938fc733fccb 100644 > --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c > +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c > @@ -492,11 +492,12 @@ nfp_eth_set_bit_config(struct nfp_nsp *nsp, > unsigned int raw_idx, > return 0; > } > > -#define NFP_ETH_SET_BIT_CONFIG(nsp, raw_idx, mask, val, ctrl_bit) \ > - ({ \ > - __BF_FIELD_CHECK(mask, 0ULL, val, "NFP_ETH_SET_BIT_CONFIG: "); \ > - nfp_eth_set_bit_config(nsp, raw_idx, mask, __bf_shf(mask), \ > - val, ctrl_bit); \ > +#define NFP_ETH_SET_BIT_CONFIG(nsp, raw_idx, mask, val, ctrl_bit) > \ > + ({ > \ > + __BF_FIELD_CHECK_MASK(mask, "NFP_ETH_SET_BIT_CONFIG: > "); \ > + __BF_FIELD_CHECK_VAL(mask, val, > "NFP_ETH_SET_BIT_CONFIG: "); \ > + nfp_eth_set_bit_config(nsp, raw_idx, mask, > __bf_shf(mask), \ > + val, ctrl_bit); > \ > }) > > /** > diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h > index 48ea093ff04c..79651867beb3 100644 > --- a/include/linux/bitfield.h > +++ b/include/linux/bitfield.h > @@ -41,18 +41,26 @@ > > #define __bf_shf(x) (__builtin_ffsll(x) - 1) > > -#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \ > +#define __BF_FIELD_CHECK_MASK(_mask, _pfx) \ > ({ \ > BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \ > _pfx "mask is not constant"); \ > BUILD_BUG_ON_MSG((_mask) == 0, _pfx "mask is zero"); \ > + __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \ > + (1ULL << __bf_shf(_mask))); \ > + }) > + > +#define __BF_FIELD_CHECK_VAL(_mask, _val, _pfx) > \ > + ({ \ > BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ? \ > ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \ > _pfx "value too large for the field"); \ > - BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull, \ > + }) > + > +#define __BF_FIELD_CHECK_REG(_mask, _reg, _pfx) > \ > + ({ \ > + BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ULL, \ > _pfx "type of reg too small for mask"); \ > - __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \ > - (1ULL << __bf_shf(_mask))); \ > }) > > /** > @@ -64,7 +72,7 @@ > */ > #define FIELD_MAX(_mask) \ > ({ \ > - __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_MAX: "); \ > + __BF_FIELD_CHECK_MASK(_mask, "FIELD_MAX: "); \ > (typeof(_mask))((_mask) >> __bf_shf(_mask)); \ > }) > > @@ -77,7 +85,7 @@ > */ > #define FIELD_FIT(_mask, _val) \ > ({ \ > - __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_FIT: "); \ > + __BF_FIELD_CHECK_MASK(_mask, "FIELD_FIT: "); \ > !((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \ > }) > @@ -91,7 +99,8 @@ > */ > #define FIELD_PREP(_mask, _val) > \ > ({ \ > - __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \ > + __BF_FIELD_CHECK_MASK(_mask, "FIELD_PREP: "); \ > + __BF_FIELD_CHECK_VAL(_mask, _val, "FIELD_PREP: "); \ > ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ > }) > > @@ -105,7 +114,8 @@ > */ > #define FIELD_GET(_mask, _reg) \ > ({ \ > - __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \ > + __BF_FIELD_CHECK_MASK(_mask, "FIELD_GET: "); \ > + __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \ > (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ > }) > > > >> >> That's all--just questions, I have no problem with the patch... >> >> -Alex >> >> >> >> >>> FIELD_FIT() should return true or false at runtime for whether a value >>> can fit for not. Don't break the build over a value that's too large for >>> the mask. We'd prefer to keep the inlining and compiler optimizations >>> though we know this case will always return false. >>> >>> Cc: stable@xxxxxxxxxxxxxxx >>> Link: https://lore.kernel.org/kernel-hardening/CAK7LNASvb0UDJ0U5wkYYRzTAdnEs64HjXpEUL7d=V0CXiAXcNw@xxxxxxxxxxxxxx/ >>> Reported-by: Masahiro Yamada <masahiroy@xxxxxxxxxx> >>> Debugged-by: Sami Tolvanen <samitolvanen@xxxxxxxxxx> >>> Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx> >>> Signed-off-by: Nick Desaulniers <ndesaulniers@xxxxxxxxxx> >>> --- >>> include/linux/bitfield.h | 2 +- >>> 1 file changed, 1 insertion(+), 1 deletion(-) >>> >>> diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h >>> index 48ea093ff04c..4e035aca6f7e 100644 >>> --- a/include/linux/bitfield.h >>> +++ b/include/linux/bitfield.h >>> @@ -77,7 +77,7 @@ >>> */ >>> #define FIELD_FIT(_mask, _val) \ >>> ({ \ >>> - __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_FIT: "); \ >>> + __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_FIT: "); \ >>> !((((typeof(_mask))_val) << __bf_shf(_mask)) & ~(_mask)); \ >>> }) >>> >>> >> > > > -- > Thanks, > ~Nick Desaulniers >