]> www.infradead.org Git - users/hch/misc.git/commitdiff
of: address: Unify resource bounds overflow checking
authorThomas Weißschuh <thomas.weissschuh@linutronix.de>
Fri, 6 Sep 2024 12:25:19 +0000 (14:25 +0200)
committerRob Herring (Arm) <robh@kernel.org>
Fri, 13 Sep 2024 19:00:52 +0000 (14:00 -0500)
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>
Link: https://lore.kernel.org/r/20240906-of-address-overflow-v1-1-19567aaa61da@linutronix.de
[robh: Fix to not return error on 0 sized resource]
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
drivers/of/address.c

index 7e59283a44723c9b80a7660eb562f7d2da1101c5..286f0c161e332f29d84921455c6e1aeb52275a7f 100644 (file)
@@ -198,6 +198,23 @@ 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 && 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 +233,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 +250,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 +270,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 +1073,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);
 }
 
 /**