On Fri, Jan 17, 2025 at 12:23:53PM +0530, Basharath Hussain Khaja wrote:
> >> >> Thomas Weißschuh <thomas.weisssc...@linutronix.de> writes:
> >> >> > The members "start" and "end" of struct resource are of type
> >> >> > "resource_size_t" which can be 32bit wide.
> >> >> > Values read from OF however are always 64bit wide.
> >> >> >
> >> >> > Refactor the diff overflow checks into a helper function.
> >> >> > Also extend the checks to validate each calculation step.
> >> >> >
> >> >> > Signed-off-by: Thomas Weißschuh <thomas.weisssc...@linutronix.de>
> >> >> > ---
> >> >> >  drivers/of/address.c | 45 
> >> >> > ++++++++++++++++++++++++++-------------------
> >> >> >  1 file changed, 26 insertions(+), 19 deletions(-)
> >> >> >
> >> >> > diff --git a/drivers/of/address.c b/drivers/of/address.c
> >> >> > index 7e59283a4472..df854bb427ce 100644
> >> >> > --- a/drivers/of/address.c
> >> >> > +++ b/drivers/of/address.c
> >> >> > @@ -198,6 +198,25 @@ static u64 of_bus_pci_map(__be32 *addr, const 
> >> >> > __be32
> >> >> > *range, int na, int ns,
> >> >> >
> >> >> >  #endif /* CONFIG_PCI */
> >> >> >
> >> >> > +static int __of_address_resource_bounds(struct resource *r, u64 
> >> >> > start, u64
> >> >> > size)
> >> >> > +{
> >> >> > +     u64 end = start;
> >> >> > +
> >> >> > +     if (overflows_type(start, r->start))
> >> >> > +             return -EOVERFLOW;
> >> >> > +     if (size == 0)
> >> >> > +             return -EOVERFLOW;
> >> >> > +     if (check_add_overflow(end, size - 1, &end))
> >> >> > +             return -EOVERFLOW;
> >> >> > +     if (overflows_type(end, r->end))
> >> >> > +             return -EOVERFLOW;
> >> >>
> >> >> This breaks PCI on powerpc qemu. Part of the PCI probe reads a resource
> >> >> that's zero sized, which used to succeed but now fails due to the size
> >> >> check above.
> >> >>
> >> >> The diff below fixes it for me.
> >> >
> >> > I fixed it up with your change.
> >>
> >>
> >> This commit is breaking Ethernet functionality on the TI AM57xx platform 
> >> due to
> >> zero byte SRAM block size allocation during initialization. Prior to this
> >> patch, zero byte block sizes were handled properly.
> > 
> > What driver and where exactly?
> 
> We found an issue while developing the driver [1] and more
> specifically in [2] (lines 313-327), but it looks like this is a
> generic issue which can block 1 byte of memory, when a zero size
> request has been initiated for the reserved region.
>
> static int __of_address_resource_bounds(struct resource *r, u64 start, u64 
> size)
> {
>     u64 end = start;
> 
>     if (overflows_type(start, r->start))
>         return -EOVERFLOW;
>     if (size && check_add_overflow(end, size - 1, &end))
>         return -EOVERFLOW;
>     if (overflows_type(end, r->end))
>         return -EOVERFLOW;
> 
>     r->start = start;
>     r->end = end;
> 
>     return 0;
> }
> 
> Though we have the start address handling already in place above, we
> do see an issue with the end address, because there is an
> unconditional +1 afterwards in resource_size() API below which is
> responsible for reserving the extra byte
> 
> static inline resource_size_t resource_size(const struct resource *res)
> {
>         return res->end - res->start + 1;
> }

Now the report makes more sense.

> We have 4 ways of fixing it.
> 
> Option 1: Modify the function to handle the size zero case
> 
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index c1f1c810e810..8db6ae9a12b8 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -204,6 +204,12 @@ static int __of_address_resource_bounds(struct resource 
> *r, u64 start, u64 size)
>  
>         if (overflows_type(start, r->start))
>                 return -EOVERFLOW;
> +       if (!size) {
> +               r->start = start;
> +               r->end = end - 1;
> +
> +               return 0;
> +       }
>         if (size && check_add_overflow(end, size - 1, &end))
>                 return -EOVERFLOW;
>         if (overflows_type(end, r->end))
> 
> This seems to be the simplest solution.

Fixing it in __of_address_resource_bounds() looks correct to me.
The proposed solution doesn't look as clean as I'd like though,
this is highly subjective, though.

What about the following (untested)?

static int __of_address_resource_bounds(struct resource *r, u64 start, u64 size)
{
        if (overflows_type(start, r->start))
                return -EOVERFLOW;

        r->start = start;
        r->end = start;

        if (!size)
                r->end -= 1; /* May underflow for empty resources. */
        else if (check_add_overflow(r->end, size - 1, &r->end))
                return -EOVERFLOW;

        return 0;
}

A kunit test looks to be in order in any case, to make sure all the
edgecases are handled.

> Option 2: Handle in resource_size().
> static inline resource_size_t resource_size(const struct resource *res)
> {      
>           return  (res->end == res->start) ? 0 : (res->end - res->start + 1);
> }
> There are plenty of places where we are using this API and there is an 
> assumption that the end address should always be start + size -1. We are a 
> bit unsure about the side effects of this change.
> 
> Option 3: Handle in sram_reserve_region().
> We can avoid calling the resource_size() API and handle size zero as a 
> special case. We are a bit unsure about the side effects of this change as 
> well.
> 
> Option 4: Handle this in dts [2] with non zero size. Estimate the approximate 
> size and update that value in dts file with extra buffer. However, as 
> indicated in [2] in lines 313-327, the size is not known apriori and the 
> actual size is only known in runtime. So if we set some size for this buffer, 
> then this will always be blocked and may or may not be used subsequently.
> 
> [1] https://lore.kernel.org/all/20250109105600.41297-1-bashar...@couthit.com/
> [2] 
> https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/ti/omap/dra7.dtsi
> 
> 
> Thanks & Best Regards,
> Basharath

Reply via email to