diff mbox series

of: address: Unify resource bounds overflow checking

Message ID 20240906-of-address-overflow-v1-1-19567aaa61da@linutronix.de
State Not Applicable
Headers show
Series of: address: Unify resource bounds overflow checking | expand

Checks

Context Check Description
robh/checkpatch success
robh/patch-applied fail build log

Commit Message

Thomas Weißschuh Sept. 6, 2024, 12:25 p.m. UTC
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.weissschuh@linutronix.de>
---
 drivers/of/address.c | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)


---
base-commit: 20e5e74b91e99d57059d783e910d08a65573458f
change-id: 20240906-of-address-overflow-06b16d785ba7

Best regards,

Comments

Rob Herring Sept. 6, 2024, 9:34 p.m. UTC | #1
On Fri, 06 Sep 2024 14:25:19 +0200, Thomas Weißschuh wrote:
> 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.weissschuh@linutronix.de>
> ---
>  drivers/of/address.c | 45 ++++++++++++++++++++++++++-------------------
>  1 file changed, 26 insertions(+), 19 deletions(-)
> 

Applied, thanks!
Michael Ellerman Sept. 13, 2024, 1:15 p.m. UTC | #2
Thomas Weißschuh <thomas.weissschuh@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.weissschuh@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.

It leaves r.end == r.start, which is fine in my case, because the code
only uses r.start.

And it seems more sane than the old code which would return
end = start - 1, for zero sized resources.

cheers


diff --git a/drivers/of/address.c b/drivers/of/address.c
index df854bb427ce..a001e789a6c4 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -204,9 +204,7 @@ static int __of_address_resource_bounds(struct resource *r, u64 start, u64 size)
 
 	if (overflows_type(start, r->start))
 		return -EOVERFLOW;
-	if (size == 0)
-		return -EOVERFLOW;
-	if (check_add_overflow(end, size - 1, &end))
+	if (size > 0 && check_add_overflow(end, size - 1, &end))
 		return -EOVERFLOW;
 	if (overflows_type(end, r->end))
 		return -EOVERFLOW;
Rob Herring Sept. 13, 2024, 6:56 p.m. UTC | #3
On Fri, Sep 13, 2024 at 8:15 AM Michael Ellerman <mpe@ellerman.id.au> wrote:
>
> Thomas Weißschuh <thomas.weissschuh@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.weissschuh@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.


> It leaves r.end == r.start, which is fine in my case, because the code
> only uses r.start.
>
> And it seems more sane than the old code which would return
> end = start - 1, for zero sized resources.
>
> cheers
>
>
> diff --git a/drivers/of/address.c b/drivers/of/address.c
> index df854bb427ce..a001e789a6c4 100644
> --- a/drivers/of/address.c
> +++ b/drivers/of/address.c
> @@ -204,9 +204,7 @@ static int __of_address_resource_bounds(struct resource *r, u64 start, u64 size)
>
>         if (overflows_type(start, r->start))
>                 return -EOVERFLOW;
> -       if (size == 0)
> -               return -EOVERFLOW;
> -       if (check_add_overflow(end, size - 1, &end))
> +       if (size > 0 && check_add_overflow(end, size - 1, &end))
>                 return -EOVERFLOW;
>         if (overflows_type(end, r->end))
>                 return -EOVERFLOW;
>
Michael Ellerman Sept. 13, 2024, 11:10 p.m. UTC | #4
Rob Herring <robh@kernel.org> writes:
> On Fri, Sep 13, 2024 at 8:15 AM Michael Ellerman <mpe@ellerman.id.au> wrote:
>> Thomas Weißschuh <thomas.weissschuh@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.weissschuh@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.

Thanks.

cheers
diff mbox series

Patch

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;
+
+	r->start = start;
+	r->end = end;
+
+	return 0;
+}
+
 /*
  * of_pci_range_to_resource - Create a resource from an of_pci_range
  * @range:	the PCI range that describes the resource
@@ -216,6 +235,7 @@  static u64 of_bus_pci_map(__be32 *addr, const __be32 *range, int na, int ns,
 int of_pci_range_to_resource(struct of_pci_range *range,
 			     struct device_node *np, struct resource *res)
 {
+	u64 start;
 	int err;
 	res->flags = range->flags;
 	res->parent = res->child = res->sibling = NULL;
@@ -232,18 +252,11 @@  int of_pci_range_to_resource(struct of_pci_range *range,
 			err = -EINVAL;
 			goto invalid_range;
 		}
-		res->start = port;
+		start = port;
 	} else {
-		if ((sizeof(resource_size_t) < 8) &&
-		    upper_32_bits(range->cpu_addr)) {
-			err = -EINVAL;
-			goto invalid_range;
-		}
-
-		res->start = range->cpu_addr;
+		start = range->cpu_addr;
 	}
-	res->end = res->start + range->size - 1;
-	return 0;
+	return __of_address_resource_bounds(res, start, range->size);
 
 invalid_range:
 	res->start = (resource_size_t)OF_BAD_ADDR;
@@ -259,8 +272,8 @@  EXPORT_SYMBOL(of_pci_range_to_resource);
  * @res:	pointer to a valid resource that will be updated to
  *              reflect the values contained in the range.
  *
- * Returns ENOENT if the entry is not found or EINVAL if the range cannot be
- * converted to resource.
+ * Returns -ENOENT if the entry is not found or -EOVERFLOW if the range
+ * cannot be converted to resource.
  */
 int of_range_to_resource(struct device_node *np, int index, struct resource *res)
 {
@@ -1062,16 +1075,10 @@  static int __of_address_to_resource(struct device_node *dev, int index, int bar_
 	if (of_mmio_is_nonposted(dev))
 		flags |= IORESOURCE_MEM_NONPOSTED;
 
-	if (overflows_type(taddr, r->start))
-		return -EOVERFLOW;
-	r->start = taddr;
-	if (overflows_type(taddr + size - 1, r->end))
-		return -EOVERFLOW;
-	r->end = taddr + size - 1;
 	r->flags = flags;
 	r->name = name ? name : dev->full_name;
 
-	return 0;
+	return __of_address_resource_bounds(r, taddr, size);
 }
 
 /**