Re: [PATCH] mm: page_ext: validate section in for_each_page_ext iterator
From: Ketan Kishore
Date: Fri Jun 19 2026 - 16:44:37 EST
On 6/19/2026 7:08 PM, Matthew Wilcox wrote:
On Fri, Jun 19, 2026 at 10:00:59AM +0200, David Hildenbrand (Arm) wrote:
Likely we should just handle that in the iterator, looking up page-ext for something
that doesn't exist is weird.
Yes, but I prefer to pass 'max' to the _next step explicitly, rather
than put it in the iterator struct. Untested patch attached.
The other way to do it is s/index/remaining/ and count it down rather
than upwards. But that assumes none of the iterator users look at
iter->index, and I don't have the patience to check that.
I find it weird that somebody botheresd to add kernel-doc for
page_ext_iter_begin/next. They're internal implementation and really
shouldn't have public documentation.
diff --git a/include/linux/page_ext.h b/include/linux/page_ext.h
index 61e876e255e8..4f7d7a8709de 100644
--- a/include/linux/page_ext.h
+++ b/include/linux/page_ext.h
@@ -120,14 +120,18 @@ struct page_ext_iter {
* page_ext_iter_begin() - Prepare for iterating through page extensions.
* @iter: page extension iterator.
* @pfn: PFN of the page we're interested in.
+ * @count: maximum number of page extensions to return.
*
* Must be called with RCU read lock taken.
*
* Return: NULL if no page_ext exists for this page.
*/
static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter,
- unsigned long pfn)
+ unsigned long pfn, unsigned long count)
{
+ if (count == 0)
+ return NULL;
+
iter->index = 0;
iter->start_pfn = pfn;
iter->page_ext = page_ext_lookup(pfn);
@@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_iter_begin(struct page_ext_iter *iter,
/**
* page_ext_iter_next() - Get next page extension
* @iter: page extension iterator.
+ * @count: maximum number of page extensions to return.
*
* Must be called with RCU read lock taken.
*
* Return: NULL if no next page_ext exists.
*/
-static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter)
+static inline struct page_ext *page_ext_iter_next(struct page_ext_iter *iter,
+ unsigned long count)
{
unsigned long pfn;
if (WARN_ON_ONCE(!iter->page_ext))
return NULL;
- iter->index++;
+ if (iter->index++ >= count)
+ return NULL;
pfn = iter->start_pfn + iter->index;
if (page_ext_iter_next_fast_possible(pfn))
@@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_iter_get(const struct page_ext_iter *ite
* IMPORTANT: must be called with RCU read lock taken.
*/
#define for_each_page_ext(__page, __pgcount, __page_ext, __iter) \
- for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\
- __page_ext && __iter.index < __pgcount; \
- __page_ext = page_ext_iter_next(&__iter))
+ for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \
+ __page_ext; \
+ __page_ext = page_ext_iter_next(&__iter, __pgcount))
#else /* !CONFIG_PAGE_EXTENSION */
struct page_ext;
Thanks Matthew for the suggestions and the proposed alternate fix.
I have tested both your and David's patch, the reported issue is not observed anymore with both the suggested patches.
Since, in the reply from David on your suggested fix he agrees with the changes as they are fixing the issue in a similar way.
I'll go ahead and post the v2 with your suggestions.
Thanks & Regards,
Ketan