git: da92ecbc0d8f - main - vm_phys: fix seg->end test in alloc_seg_contig

From: Doug Moore <dougm_at_FreeBSD.org>
Date: Tue, 18 Jan 2022 18:49:20 UTC
The branch main has been updated by dougm:

URL: https://cgit.FreeBSD.org/src/commit/?id=da92ecbc0d8f4652cd5c97aace290103055c7019

commit da92ecbc0d8f4652cd5c97aace290103055c7019
Author:     Doug Moore <dougm@FreeBSD.org>
AuthorDate: 2022-01-18 18:32:46 +0000
Commit:     Doug Moore <dougm@FreeBSD.org>
CommitDate: 2022-01-18 18:49:09 +0000

    vm_phys: fix seg->end test in alloc_seg_contig
    
    In vm_phys_alloc_seg_contig, in allocating multiple memory blocks for
    a huge allocation, ensure that the end of the allocated range does not
    exceed the upper segment limit.
    
    Reorder a couple of checks to improve code layout.
    
    Reviewed by:    alc
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D33870
---
 sys/vm/vm_phys.c | 69 +++++++++++++++++++++++++++-----------------------------
 1 file changed, 33 insertions(+), 36 deletions(-)

diff --git a/sys/vm/vm_phys.c b/sys/vm/vm_phys.c
index 6798c55cc91c..a880b7abd324 100644
--- a/sys/vm/vm_phys.c
+++ b/sys/vm/vm_phys.c
@@ -1428,47 +1428,44 @@ vm_phys_alloc_seg_contig(struct vm_phys_seg *seg, u_long npages,
 			fl = (*seg->free_queues)[pind];
 			TAILQ_FOREACH(m_ret, &fl[oind].pl, listq) {
 				/*
-				 * Is the size of this allocation request
-				 * larger than the largest block size?
+				 * Determine if the address range starting at pa
+				 * is within the given range, satisfies the
+				 * given alignment, and does not cross the given
+				 * boundary.
 				 */
-				if (order >= VM_NFREEORDER) {
-					/*
-					 * Determine if a sufficient number of
-					 * subsequent blocks to satisfy the
-					 * allocation request are free.
-					 */
-					pa = VM_PAGE_TO_PHYS(m_ret);
-					pa_end = pa + size;
-					if (pa_end < pa)
-						continue;
-					for (;;) {
-						pa += 1 << (PAGE_SHIFT +
-						    VM_NFREEORDER - 1);
-						if (pa >= pa_end ||
-						    pa < seg->start ||
-						    pa >= seg->end)
-							break;
-						m = &seg->first_page[atop(pa -
-						    seg->start)];
-						if (m->order != VM_NFREEORDER -
-						    1)
-							break;
-					}
-					/* If not, go to the next block. */
-					if (pa < pa_end)
-						continue;
-				}
+				pa = VM_PAGE_TO_PHYS(m_ret);
+				pa_end = pa + size;
+				if (pa < low || pa_end > high ||
+				    !vm_addr_ok(pa, size, alignment, boundary))
+					continue;
 
 				/*
-				 * Determine if the blocks are within the
-				 * given range, satisfy the given alignment,
-				 * and do not cross the given boundary.
+				 * Is the size of this allocation request
+				 * no more than the largest block size?
 				 */
-				pa = VM_PAGE_TO_PHYS(m_ret);
-				pa_end = pa + size;
-				if (pa >= low && pa_end <= high &&
-				    vm_addr_ok(pa, size, alignment, boundary))
+				if (order < VM_NFREEORDER)
 					goto done;
+
+				/*
+				 * Determine if the address range is valid
+				 * (without overflow in pa_end calculation)
+				 * and fits within the segment.
+				 */
+				if (pa_end < pa || pa_end > seg->end)
+					continue;
+
+				/*
+				 * Determine if a sufficient number of
+				 * subsequent blocks to satisfy the
+				 * allocation request are free.
+				 */
+				do {
+					pa += 1 <<
+					    (PAGE_SHIFT + VM_NFREEORDER - 1);
+					if (pa >= pa_end)
+						goto done;
+				} while (VM_NFREEORDER - 1 == seg->first_page[
+				    atop(pa - seg->start)].order);
 			}
 		}
 	}