2023-05-15 13:29:22

by Ruihan Li

[permalink] [raw]
Subject: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

The current uses of PageAnon in page table check functions can lead to
type confusion bugs between struct page and slab [1], if slab pages are
accidentally mapped into the user space. This is because slab reuses the
bits in struct page to store its internal states, which renders PageAnon
ineffective on slab pages.

Since slab pages are not expected to be mapped into the user space, this
patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages
are not inadvertently mapped. Otherwise, there must be some bugs in the
kernel.

Reported-by: [email protected]
Closes: https://lore.kernel.org/lkml/[email protected]/ [1]
Fixes: df4e817b7108 ("mm: page table check")
Cc: <[email protected]> # 5.17
Signed-off-by: Ruihan Li <[email protected]>
---
include/linux/page-flags.h | 6 ++++++
mm/page_table_check.c | 6 ++++++
2 files changed, 12 insertions(+)

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 1c68d67b8..92a2063a0 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -617,6 +617,12 @@ PAGEFLAG_FALSE(VmemmapSelfHosted, vmemmap_self_hosted)
* Please note that, confusingly, "page_mapping" refers to the inode
* address_space which maps the page from disk; whereas "page_mapped"
* refers to user virtual address space into which the page is mapped.
+ *
+ * For slab pages, since slab reuses the bits in struct page to store its
+ * internal states, the page->mapping does not exist as such, nor do these
+ * flags below. So in order to avoid testing non-existent bits, please
+ * make sure that PageSlab(page) actually evaluates to false before calling
+ * the following functions (e.g., PageAnon). See mm/slab.h.
*/
#define PAGE_MAPPING_ANON 0x1
#define PAGE_MAPPING_MOVABLE 0x2
diff --git a/mm/page_table_check.c b/mm/page_table_check.c
index 25d8610c0..f2baf97d5 100644
--- a/mm/page_table_check.c
+++ b/mm/page_table_check.c
@@ -71,6 +71,8 @@ static void page_table_check_clear(struct mm_struct *mm, unsigned long addr,

page = pfn_to_page(pfn);
page_ext = page_ext_get(page);
+
+ BUG_ON(PageSlab(page));
anon = PageAnon(page);

for (i = 0; i < pgcnt; i++) {
@@ -107,6 +109,8 @@ static void page_table_check_set(struct mm_struct *mm, unsigned long addr,

page = pfn_to_page(pfn);
page_ext = page_ext_get(page);
+
+ BUG_ON(PageSlab(page));
anon = PageAnon(page);

for (i = 0; i < pgcnt; i++) {
@@ -133,6 +137,8 @@ void __page_table_check_zero(struct page *page, unsigned int order)
struct page_ext *page_ext;
unsigned long i;

+ BUG_ON(PageSlab(page));
+
page_ext = page_ext_get(page);
BUG_ON(!page_ext);
for (i = 0; i < (1ul << order); i++) {
--
2.40.1



2023-05-15 16:41:33

by Pasha Tatashin

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

On Mon, May 15, 2023 at 9:10 AM Ruihan Li <[email protected]> wrote:
>
> The current uses of PageAnon in page table check functions can lead to
> type confusion bugs between struct page and slab [1], if slab pages are
> accidentally mapped into the user space. This is because slab reuses the
> bits in struct page to store its internal states, which renders PageAnon
> ineffective on slab pages.
>
> Since slab pages are not expected to be mapped into the user space, this
> patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages
> are not inadvertently mapped. Otherwise, there must be some bugs in the
> kernel.
>
> Reported-by: [email protected]
> Closes: https://lore.kernel.org/lkml/[email protected]/ [1]
> Fixes: df4e817b7108 ("mm: page table check")
> Cc: <[email protected]> # 5.17
> Signed-off-by: Ruihan Li <[email protected]>

Acked-by: Pasha Tatashin <[email protected]>

I would also update order in mm/memory.c
static int validate_page_before_insert(struct page *page)
{
if (PageAnon(page) || PageSlab(page) || page_has_type(page))

It is not strictly a bug there, as it works by accident, but
PageSlab() should go before PageAnon(), because without checking if
this is PageSlab() we should not be testing for PageAnon().

Thanks you,
Pasha

2023-05-16 12:34:11

by Ruihan Li

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

On Mon, May 15, 2023 at 12:28:54PM -0400, Pasha Tatashin wrote:
>
> On Mon, May 15, 2023 at 9:10 AM Ruihan Li <[email protected]> wrote:
> >
> > The current uses of PageAnon in page table check functions can lead to
> > type confusion bugs between struct page and slab [1], if slab pages are
> > accidentally mapped into the user space. This is because slab reuses the
> > bits in struct page to store its internal states, which renders PageAnon
> > ineffective on slab pages.
> >
> > Since slab pages are not expected to be mapped into the user space, this
> > patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages
> > are not inadvertently mapped. Otherwise, there must be some bugs in the
> > kernel.
> >
> > Reported-by: [email protected]
> > Closes: https://lore.kernel.org/lkml/[email protected]/ [1]
> > Fixes: df4e817b7108 ("mm: page table check")
> > Cc: <[email protected]> # 5.17
> > Signed-off-by: Ruihan Li <[email protected]>
>
> Acked-by: Pasha Tatashin <[email protected]>
>
> I would also update order in mm/memory.c
> static int validate_page_before_insert(struct page *page)
> {
> if (PageAnon(page) || PageSlab(page) || page_has_type(page))
>
> It is not strictly a bug there, as it works by accident, but
> PageSlab() should go before PageAnon(), because without checking if
> this is PageSlab() we should not be testing for PageAnon().

Right. Perhaps it would be better to send another patch for this
separately.

>
> Thanks you,
> Pasha

Thanks,
Ruihan Li


2023-05-16 12:57:26

by David Hildenbrand

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

On 16.05.23 13:51, Ruihan Li wrote:
> On Mon, May 15, 2023 at 12:28:54PM -0400, Pasha Tatashin wrote:
>>
>> On Mon, May 15, 2023 at 9:10 AM Ruihan Li <[email protected]> wrote:
>>>
>>> The current uses of PageAnon in page table check functions can lead to
>>> type confusion bugs between struct page and slab [1], if slab pages are
>>> accidentally mapped into the user space. This is because slab reuses the
>>> bits in struct page to store its internal states, which renders PageAnon
>>> ineffective on slab pages.
>>>
>>> Since slab pages are not expected to be mapped into the user space, this
>>> patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages
>>> are not inadvertently mapped. Otherwise, there must be some bugs in the
>>> kernel.
>>>
>>> Reported-by: [email protected]
>>> Closes: https://lore.kernel.org/lkml/[email protected]/ [1]
>>> Fixes: df4e817b7108 ("mm: page table check")
>>> Cc: <[email protected]> # 5.17
>>> Signed-off-by: Ruihan Li <[email protected]>
>>
>> Acked-by: Pasha Tatashin <[email protected]>
>>
>> I would also update order in mm/memory.c
>> static int validate_page_before_insert(struct page *page)
>> {
>> if (PageAnon(page) || PageSlab(page) || page_has_type(page))
>>
>> It is not strictly a bug there, as it works by accident, but
>> PageSlab() should go before PageAnon(), because without checking if
>> this is PageSlab() we should not be testing for PageAnon().
>
> Right. Perhaps it would be better to send another patch for this
> separately.

Probably not really worth it IMHO. With PageSlab() we might have
PageAnon() false-positives. Either will take the same path here ...

On a related note, stable_page_flags() checks PageKsm()/PageAnon()
without caring about PageSlab().

At least it's just a debugging interface and will indicate KPF_SLAB in
any case as well ...

--
Thanks,

David / dhildenb


2023-05-16 14:22:58

by Pasha Tatashin

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

> >> Acked-by: Pasha Tatashin <[email protected]>
> >>
> >> I would also update order in mm/memory.c
> >> static int validate_page_before_insert(struct page *page)
> >> {
> >> if (PageAnon(page) || PageSlab(page) || page_has_type(page))
> >>
> >> It is not strictly a bug there, as it works by accident, but
> >> PageSlab() should go before PageAnon(), because without checking if
> >> this is PageSlab() we should not be testing for PageAnon().
> >
> > Right. Perhaps it would be better to send another patch for this
> > separately.

Yes, as a separate from this series patch would work.

>
> Probably not really worth it IMHO. With PageSlab() we might have
> PageAnon() false-positives. Either will take the same path here ...

That is correct, it works by accident, but it is not a good idea to
keep a broken logic at least because it may be copied into other
places.

2023-05-16 14:36:48

by Ruihan Li

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] mm: page_table_check: Ensure user pages are not slab pages

On Tue, May 16, 2023 at 02:54:04PM +0200, David Hildenbrand wrote:
>
> On 16.05.23 13:51, Ruihan Li wrote:
> > On Mon, May 15, 2023 at 12:28:54PM -0400, Pasha Tatashin wrote:
> > >
> > > On Mon, May 15, 2023 at 9:10 AM Ruihan Li <[email protected]> wrote:
> > > >
> > > > The current uses of PageAnon in page table check functions can lead to
> > > > type confusion bugs between struct page and slab [1], if slab pages are
> > > > accidentally mapped into the user space. This is because slab reuses the
> > > > bits in struct page to store its internal states, which renders PageAnon
> > > > ineffective on slab pages.
> > > >
> > > > Since slab pages are not expected to be mapped into the user space, this
> > > > patch adds BUG_ON(PageSlab(page)) checks to make sure that slab pages
> > > > are not inadvertently mapped. Otherwise, there must be some bugs in the
> > > > kernel.
> > > >
> > > > Reported-by: [email protected]
> > > > Closes: https://lore.kernel.org/lkml/[email protected]/ [1]
> > > > Fixes: df4e817b7108 ("mm: page table check")
> > > > Cc: <[email protected]> # 5.17
> > > > Signed-off-by: Ruihan Li <[email protected]>
> > >
> > > Acked-by: Pasha Tatashin <[email protected]>
> > >
> > > I would also update order in mm/memory.c
> > > static int validate_page_before_insert(struct page *page)
> > > {
> > > if (PageAnon(page) || PageSlab(page) || page_has_type(page))
> > >
> > > It is not strictly a bug there, as it works by accident, but
> > > PageSlab() should go before PageAnon(), because without checking if
> > > this is PageSlab() we should not be testing for PageAnon().
> >
> > Right. Perhaps it would be better to send another patch for this
> > separately.
>
> Probably not really worth it IMHO. With PageSlab() we might have PageAnon()
> false-positives. Either will take the same path here ...

Well, I'm not against that. If just fixing this one doesn't look
worthwhile, I'm not sure if anyone wishes to find and clean up all these
"misuses" altogether, though that's certainly a low-priority task if
nothing is actually broken.

>
> On a related note, stable_page_flags() checks PageKsm()/PageAnon() without
> caring about PageSlab().
>
> At least it's just a debugging interface and will indicate KPF_SLAB in any
> case as well ...

I just went through that function quickly, and found that PageHuge also
seems to be accessing non-existent fields (folio->_folio_dtor) on slab
pages. Again, nothing is really broken.

>
> --
> Thanks,
>
> David / dhildenb

Thanks,
Ruihan Li