On Tue, Mar 30, 2021 at 05:56:45PM +0800, Bin Meng wrote: > With LLVM 10.0.0+, the following codes in fdt_num_mem_rsv() does not > work any more for an fdt that is at address 0: > > for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { > if (fdt64_ld_(&re->size) == 0) > return i; > } > > Due to LLVM's optimization engine utilizing a UB in C, the following > code pattern: > > if ((pointer + offset) != NULL) > > is transformed into: > > if (pointer != NULL) > > because if pointer is NULL and offset is non-zero, the result of > (pointer + offset) is UB. So LLVM is free to exploit such UB to > perform some optimization. > > In this case, fdt_mem_rsv() gets inlined and returns (pointer + offset). > And LLVM in turns emits codes to check fdt against NULL, which won't > work for fdt at address 0. I don't think this really fixes anything. It might fool LLVM into doing what you need right now, but I don't see any reason to expect it will keep doing so. IIUC, you're saying that the specific problem is that adding a non-zero offset to a NULL pointer is UB, which happens inside fdt_mem_rsv_() if n != 0. But with your patch, that UB still exists.. > > Signed-off-by: Bin Meng <bmeng.cn@xxxxxxxxx> > --- > > libfdt/fdt_ro.c | 17 +++++++++++++---- > 1 file changed, 13 insertions(+), 4 deletions(-) > > diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c > index 17584da..4db4013 100644 > --- a/libfdt/fdt_ro.c > +++ b/libfdt/fdt_ro.c > @@ -157,18 +157,26 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle) > return 0; > } > > -static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) > +static bool fdt_is_mem_rsv(const void *fdt, int n) > { > unsigned int offset = n * sizeof(struct fdt_reserve_entry); > unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; > > if (!can_assume(VALID_INPUT)) { > if (absoffset < fdt_off_mem_rsvmap(fdt)) > - return NULL; > + return false; > if (absoffset > fdt_totalsize(fdt) - > sizeof(struct fdt_reserve_entry)) > - return NULL; > + return false; > } > + > + return true; > +} > + > +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) > +{ > + if (!fdt_is_mem_rsv(fdt, n)) > + return NULL; > return fdt_mem_rsv_(fdt, n); > } > > @@ -191,7 +199,8 @@ int fdt_num_mem_rsv(const void *fdt) > int i; > const struct fdt_reserve_entry *re; > > - for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { > + for (i = 0; fdt_is_mem_rsv(fdt, i); i++) { > + re = fdt_mem_rsv_(fdt, i); .. here ^^. Basically if your compiled is going to optimized based on (NULL + something) being UB, and the NULL pointer is address 0, that's fundamentally incompatible with storing a device tree at address 0. > if (fdt64_ld_(&re->size) == 0) > return i; > } -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
Attachment:
signature.asc
Description: PGP signature