[PATCH v2 2/4] mm: internal: fix inconsistent in folio_pte_batch_flags() for shadow stack
From: Yeoreum Yun
Date: Mon Nov 02 2026 - 06:36:03 EST
In x86, shadow stack PTE is marked with PAGE_RW=0 and PAGE_DIRTY=1.
If the shadow stack pte entry is consequence with the read-only pte
like PAGE_RW=0 and PAGE_DIRTY=0, folio_pte_batch_flags()
with FPB_RESPECT_WRITE without FPB_RESPECT_DIRTY considers both are
the same.
To address this, save the first pte's writable and compare subsequence
pte's writablity with pte_write() when FPB_RESPECT_WRITE is set
without FPB_RESPECT_DIRTY.
Signed-off-by: Yeoreum Yun <yeoreum.yun@xxxxxxx>
---
mm/internal.h | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/mm/internal.h b/mm/internal.h
index 35016b1afc8bf..b4d0df4c35adf 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -418,7 +418,7 @@ static inline unsigned int folio_pte_batch_flags(struct folio *folio,
struct vm_area_struct *vma, pte_t *ptep, pte_t *ptentp,
unsigned int max_nr, fpb_t flags)
{
- bool any_writable = false, any_young = false, any_dirty = false;
+ bool any_writable = false, any_young = false, any_dirty = false, writable;
pte_t expected_pte, pte = *ptentp;
unsigned int nr, cur_nr;
@@ -438,6 +438,7 @@ static inline unsigned int folio_pte_batch_flags(struct folio *folio,
nr = pte_batch_hint(ptep, pte);
expected_pte = __pte_batch_clear_ignored(pte_advance_pfn(pte, nr), flags);
+ writable = !!pte_write(pte);
ptep = ptep + nr;
while (nr < max_nr) {
@@ -446,6 +447,16 @@ static inline unsigned int folio_pte_batch_flags(struct folio *folio,
if (!pte_same(__pte_batch_clear_ignored(pte, flags), expected_pte))
break;
+ /*
+ * x86 shadow-stack PTEs encode writability as RW=0, DIRTY=1.
+ * Without FPB_RESPECT_DIRTY, __pte_batch_clear_ignored()
+ * considers shadow-stack PTE as the same with subsequent
+ * ordinary read-only PTEs. Therefore, check whether pte writable.
+ */
+ if (unlikely((flags & (FPB_RESPECT_WRITE | FPB_RESPECT_DIRTY)) == FPB_RESPECT_WRITE &&
+ writable != !!pte_write(pte)))
+ break;
+
if (flags & FPB_MERGE_WRITE)
any_writable |= pte_write(pte);
if (flags & FPB_MERGE_YOUNG_DIRTY) {
--
2.43.0