build_crs() collects used resources without any information about
valid address windows. These resources can therefore lie outside
the interval passed to crs_replace_with_free_ranges().

crs_replace_with_free_ranges() assumed all used ranges lie within
the interval. Ranges outside it could therefore produce free ranges
extending below or above its bounds. As a result, the guest kernel
may assign a device an invalid address range.

Skip ranges with limit below the interval start or with base above
the interval's end.

Signed-off-by: Anton Kuchin <[email protected]>
---
 hw/acpi/aml-build.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 990abc64cd8b..90837b513e69 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -124,7 +124,7 @@ static gint crs_range_compare(gconstpointer a, 
gconstpointer b)
 }
 
 /*
- * crs_replace_with_free_ranges - given the 'used' ranges within [start - end]
+ * crs_replace_with_free_ranges - given the 'used' ranges and [start - end]
  * interval, computes the 'free' ranges from the same interval.
  * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function
  * will return { [base - a1], [a2 - b1], [b2 - limit] }.
@@ -140,6 +140,16 @@ void crs_replace_with_free_ranges(GPtrArray *ranges,
     for (i = 0; i < ranges->len; i++) {
         CrsRangeEntry *used = g_ptr_array_index(ranges, i);
 
+        /* skip ranges below the window */
+        if (used->limit < free_base) {
+            continue;
+        }
+
+        /* skip ranges above the window */
+        if (used->base > end) {
+            break;
+        }
+
         if (free_base < used->base) {
             crs_range_insert(free_ranges, free_base, used->base - 1);
         }

-- 
2.43.0


Reply via email to