* [PATCH] mm/gup: handle NULL pages in unpin_user_pages()
@ 2024-11-19 4:49 John Hubbard
2024-11-19 14:33 ` David Hildenbrand
0 siblings, 1 reply; 3+ messages in thread
From: John Hubbard @ 2024-11-19 4:49 UTC (permalink / raw)
To: Andrew Morton
Cc: LKML, linux-mm, John Hubbard, David Hildenbrand, Oscar Salvador,
Vivek Kasireddy, Dave Airlie, Gerd Hoffmann, Matthew Wilcox,
Christoph Hellwig, Jason Gunthorpe, Peter Xu, Arnd Bergmann,
Daniel Vetter, Dongwon Kim, Hugh Dickins, Junxiao Chang, stable
The recent addition of "pofs" (pages or folios) handling to gup has a
flaw: it assumes that unpin_user_pages() handles NULL pages in the
pages** array. That's not the case, as I discovered when I ran on a new
configuration on my test machine.
Fix this by skipping NULL pages in unpin_user_pages(), just like
unpin_folios() already does.
Details: when booting on x86 with "numa=fake=2 movablecore=4G" on Linux
6.12, and running this:
tools/testing/selftests/mm/gup_longterm
...I get the following crash:
BUG: kernel NULL pointer dereference, address: 0000000000000008
RIP: 0010:sanity_check_pinned_pages+0x3a/0x2d0
...
Call Trace:
<TASK>
? __die_body+0x66/0xb0
? page_fault_oops+0x30c/0x3b0
? do_user_addr_fault+0x6c3/0x720
? irqentry_enter+0x34/0x60
? exc_page_fault+0x68/0x100
? asm_exc_page_fault+0x22/0x30
? sanity_check_pinned_pages+0x3a/0x2d0
unpin_user_pages+0x24/0xe0
check_and_migrate_movable_pages_or_folios+0x455/0x4b0
__gup_longterm_locked+0x3bf/0x820
? mmap_read_lock_killable+0x12/0x50
? __pfx_mmap_read_lock_killable+0x10/0x10
pin_user_pages+0x66/0xa0
gup_test_ioctl+0x358/0xb20
__se_sys_ioctl+0x6b/0xc0
do_syscall_64+0x7b/0x150
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fixes: 94efde1d1539 ("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
Cc: David Hildenbrand <david@redhat.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Junxiao Chang <junxiao.chang@intel.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
Hi,
I got a nasty shock when I tried out a new test machine setup last
night--I wish I'd noticed the problem earlier! But anyway, this should
make it all better...
I've asked Greg K-H to hold off on including commit 94efde1d1539
("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
in linux-stable (6.11.y), but if this fix-to-the-fix looks good, then
maybe both fixes can ultimately end up in stable.
thanks,
John Hubbard
mm/gup.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index ad0c8922dac3..6e417502728a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -52,7 +52,12 @@ static inline void sanity_check_pinned_pages(struct page **pages,
*/
for (; npages; npages--, pages++) {
struct page *page = *pages;
- struct folio *folio = page_folio(page);
+ struct folio *folio;
+
+ if (!page)
+ continue;
+
+ folio = page_folio(page);
if (is_zero_page(page) ||
!folio_test_anon(folio))
@@ -248,9 +253,14 @@ static inline struct folio *gup_folio_range_next(struct page *start,
static inline struct folio *gup_folio_next(struct page **list,
unsigned long npages, unsigned long i, unsigned int *ntails)
{
- struct folio *folio = page_folio(list[i]);
+ struct folio *folio;
unsigned int nr;
+ if (!list[i])
+ return NULL;
+
+ folio = page_folio(list[i]);
+
for (nr = i + 1; nr < npages; nr++) {
if (page_folio(list[nr]) != folio)
break;
@@ -410,6 +420,9 @@ void unpin_user_pages(struct page **pages, unsigned long npages)
sanity_check_pinned_pages(pages, npages);
for (i = 0; i < npages; i += nr) {
folio = gup_folio_next(pages, npages, i, &nr);
+ if (!folio)
+ continue;
+
gup_put_folio(folio, nr, FOLL_PIN);
}
}
--
2.47.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] mm/gup: handle NULL pages in unpin_user_pages()
2024-11-19 4:49 [PATCH] mm/gup: handle NULL pages in unpin_user_pages() John Hubbard
@ 2024-11-19 14:33 ` David Hildenbrand
2024-11-20 3:28 ` John Hubbard
0 siblings, 1 reply; 3+ messages in thread
From: David Hildenbrand @ 2024-11-19 14:33 UTC (permalink / raw)
To: John Hubbard, Andrew Morton
Cc: LKML, linux-mm, Oscar Salvador, Vivek Kasireddy, Dave Airlie,
Gerd Hoffmann, Matthew Wilcox, Christoph Hellwig,
Jason Gunthorpe, Peter Xu, Arnd Bergmann, Daniel Vetter,
Dongwon Kim, Hugh Dickins, Junxiao Chang, stable
On 19.11.24 05:49, John Hubbard wrote:
> The recent addition of "pofs" (pages or folios) handling to gup has a
> flaw: it assumes that unpin_user_pages() handles NULL pages in the
> pages** array. That's not the case, as I discovered when I ran on a new
> configuration on my test machine.
>
> Fix this by skipping NULL pages in unpin_user_pages(), just like
> unpin_folios() already does.
>
> Details: when booting on x86 with "numa=fake=2 movablecore=4G" on Linux
> 6.12, and running this:
>
> tools/testing/selftests/mm/gup_longterm
>
> ...I get the following crash:
>
> BUG: kernel NULL pointer dereference, address: 0000000000000008
> RIP: 0010:sanity_check_pinned_pages+0x3a/0x2d0
> ...
> Call Trace:
> <TASK>
> ? __die_body+0x66/0xb0
> ? page_fault_oops+0x30c/0x3b0
> ? do_user_addr_fault+0x6c3/0x720
> ? irqentry_enter+0x34/0x60
> ? exc_page_fault+0x68/0x100
> ? asm_exc_page_fault+0x22/0x30
> ? sanity_check_pinned_pages+0x3a/0x2d0
> unpin_user_pages+0x24/0xe0
> check_and_migrate_movable_pages_or_folios+0x455/0x4b0
> __gup_longterm_locked+0x3bf/0x820
> ? mmap_read_lock_killable+0x12/0x50
> ? __pfx_mmap_read_lock_killable+0x10/0x10
> pin_user_pages+0x66/0xa0
> gup_test_ioctl+0x358/0xb20
> __se_sys_ioctl+0x6b/0xc0
> do_syscall_64+0x7b/0x150
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
> Fixes: 94efde1d1539 ("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
> Cc: David Hildenbrand <david@redhat.com>
> Cc: Oscar Salvador <osalvador@suse.de>
> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
> Cc: Dave Airlie <airlied@redhat.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Matthew Wilcox <willy@infradead.org>
> Cc: Christoph Hellwig <hch@infradead.org>
> Cc: Jason Gunthorpe <jgg@nvidia.com>
> Cc: Peter Xu <peterx@redhat.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Dongwon Kim <dongwon.kim@intel.com>
> Cc: Hugh Dickins <hughd@google.com>
> Cc: Junxiao Chang <junxiao.chang@intel.com>
> Cc: <stable@vger.kernel.org>
> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
> ---
>
> Hi,
>
> I got a nasty shock when I tried out a new test machine setup last
> night--I wish I'd noticed the problem earlier! But anyway, this should
> make it all better...
>
> I've asked Greg K-H to hold off on including commit 94efde1d1539
> ("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
> in linux-stable (6.11.y), but if this fix-to-the-fix looks good, then
> maybe both fixes can ultimately end up in stable.
>
Ouch!
> thanks,
> John Hubbard
>
> mm/gup.c | 17 +++++++++++++++--
> 1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/mm/gup.c b/mm/gup.c
> index ad0c8922dac3..6e417502728a 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -52,7 +52,12 @@ static inline void sanity_check_pinned_pages(struct page **pages,
> */
> for (; npages; npages--, pages++) {
> struct page *page = *pages;
> - struct folio *folio = page_folio(page);
> + struct folio *folio;
> +
> + if (!page)
> + continue;
> +
> + folio = page_folio(page);
>
> if (is_zero_page(page) ||
> !folio_test_anon(folio))
> @@ -248,9 +253,14 @@ static inline struct folio *gup_folio_range_next(struct page *start,
> static inline struct folio *gup_folio_next(struct page **list,
> unsigned long npages, unsigned long i, unsigned int *ntails)
> {
> - struct folio *folio = page_folio(list[i]);
> + struct folio *folio;
> unsigned int nr;
>
> + if (!list[i])
> + return NULL;
> +
I don't particularly enjoy returning NULL here, if we don't teach the
other users of that function about that possibility. There are two other
users.
Also: we are not setting "ntails" to 1. I think the callers uses that as
"nr" to advance npages. So the caller has to make sure to set "nr = 1"
in case it sees "NULL".
Alternatively ...
> + folio = page_folio(list[i]);
> +
> for (nr = i + 1; nr < npages; nr++) {
> if (page_folio(list[nr]) != folio)
> break;
> @@ -410,6 +420,9 @@ void unpin_user_pages(struct page **pages, unsigned long npages)
> sanity_check_pinned_pages(pages, npages);
> for (i = 0; i < npages; i += nr) {
... handle it here
if (!pages[i]) {
nr = 1;
continue;
}
No strong opinion. But I think we should either update all callers to
deal with returning NULL from this function, and set "nr = 1".
--
Cheers,
David / dhildenb
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] mm/gup: handle NULL pages in unpin_user_pages()
2024-11-19 14:33 ` David Hildenbrand
@ 2024-11-20 3:28 ` John Hubbard
0 siblings, 0 replies; 3+ messages in thread
From: John Hubbard @ 2024-11-20 3:28 UTC (permalink / raw)
To: David Hildenbrand, Andrew Morton
Cc: LKML, linux-mm, Oscar Salvador, Vivek Kasireddy, Dave Airlie,
Gerd Hoffmann, Matthew Wilcox, Christoph Hellwig,
Jason Gunthorpe, Peter Xu, Arnd Bergmann, Daniel Vetter,
Dongwon Kim, Hugh Dickins, Junxiao Chang, stable
On 11/19/24 6:33 AM, David Hildenbrand wrote:
> On 19.11.24 05:49, John Hubbard wrote:
>> The recent addition of "pofs" (pages or folios) handling to gup has a
>> flaw: it assumes that unpin_user_pages() handles NULL pages in the
>> pages** array. That's not the case, as I discovered when I ran on a new
>> configuration on my test machine.
>>
>> Fix this by skipping NULL pages in unpin_user_pages(), just like
>> unpin_folios() already does.
>>
>> Details: when booting on x86 with "numa=fake=2 movablecore=4G" on Linux
>> 6.12, and running this:
>>
>> tools/testing/selftests/mm/gup_longterm
>>
>> ...I get the following crash:
>>
>> BUG: kernel NULL pointer dereference, address: 0000000000000008
>> RIP: 0010:sanity_check_pinned_pages+0x3a/0x2d0
>> ...
>> Call Trace:
>> <TASK>
>> ? __die_body+0x66/0xb0
>> ? page_fault_oops+0x30c/0x3b0
>> ? do_user_addr_fault+0x6c3/0x720
>> ? irqentry_enter+0x34/0x60
>> ? exc_page_fault+0x68/0x100
>> ? asm_exc_page_fault+0x22/0x30
>> ? sanity_check_pinned_pages+0x3a/0x2d0
>> unpin_user_pages+0x24/0xe0
>> check_and_migrate_movable_pages_or_folios+0x455/0x4b0
>> __gup_longterm_locked+0x3bf/0x820
>> ? mmap_read_lock_killable+0x12/0x50
>> ? __pfx_mmap_read_lock_killable+0x10/0x10
>> pin_user_pages+0x66/0xa0
>> gup_test_ioctl+0x358/0xb20
>> __se_sys_ioctl+0x6b/0xc0
>> do_syscall_64+0x7b/0x150
>> entry_SYSCALL_64_after_hwframe+0x76/0x7e
>>
>> Fixes: 94efde1d1539 ("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
>> Cc: David Hildenbrand <david@redhat.com>
>> Cc: Oscar Salvador <osalvador@suse.de>
>> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com>
>> Cc: Dave Airlie <airlied@redhat.com>
>> Cc: Gerd Hoffmann <kraxel@redhat.com>
>> Cc: Matthew Wilcox <willy@infradead.org>
>> Cc: Christoph Hellwig <hch@infradead.org>
>> Cc: Jason Gunthorpe <jgg@nvidia.com>
>> Cc: Peter Xu <peterx@redhat.com>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
>> Cc: Dongwon Kim <dongwon.kim@intel.com>
>> Cc: Hugh Dickins <hughd@google.com>
>> Cc: Junxiao Chang <junxiao.chang@intel.com>
>> Cc: <stable@vger.kernel.org>
>> Signed-off-by: John Hubbard <jhubbard@nvidia.com>
>> ---
>>
>> Hi,
>>
>> I got a nasty shock when I tried out a new test machine setup last
>> night--I wish I'd noticed the problem earlier! But anyway, this should
>> make it all better...
>>
>> I've asked Greg K-H to hold off on including commit 94efde1d1539
>> ("mm/gup: avoid an unnecessary allocation call for FOLL_LONGTERM cases")
>> in linux-stable (6.11.y), but if this fix-to-the-fix looks good, then
>> maybe both fixes can ultimately end up in stable.
>>
>
> Ouch!
>
>> thanks,
>> John Hubbard
>>
>> mm/gup.c | 17 +++++++++++++++--
>> 1 file changed, 15 insertions(+), 2 deletions(-)
>>
>> diff --git a/mm/gup.c b/mm/gup.c
>> index ad0c8922dac3..6e417502728a 100644
>> --- a/mm/gup.c
>> +++ b/mm/gup.c
>> @@ -52,7 +52,12 @@ static inline void sanity_check_pinned_pages(struct page **pages,
>> */
>> for (; npages; npages--, pages++) {
>> struct page *page = *pages;
>> - struct folio *folio = page_folio(page);
>> + struct folio *folio;
>> +
>> + if (!page)
>> + continue;
>> +
>> + folio = page_folio(page);
>> if (is_zero_page(page) ||
>> !folio_test_anon(folio))
>> @@ -248,9 +253,14 @@ static inline struct folio *gup_folio_range_next(struct page *start,
>> static inline struct folio *gup_folio_next(struct page **list,
>> unsigned long npages, unsigned long i, unsigned int *ntails)
>> {
>> - struct folio *folio = page_folio(list[i]);
>> + struct folio *folio;
>> unsigned int nr;
>> + if (!list[i])
>> + return NULL;
>> +
>
> I don't particularly enjoy returning NULL here, if we don't teach the other users of that function about that possibility. There are two other users.
>
> Also: we are not setting "ntails" to 1. I think the callers uses that as "nr" to advance npages. So the caller has to make sure to set "nr = 1" in case it sees "NULL".
>
> Alternatively ...
>
>> + folio = page_folio(list[i]);
>> +
>> for (nr = i + 1; nr < npages; nr++) {
>> if (page_folio(list[nr]) != folio)
>> break;
>> @@ -410,6 +420,9 @@ void unpin_user_pages(struct page **pages, unsigned long npages)
>> sanity_check_pinned_pages(pages, npages);
>> for (i = 0; i < npages; i += nr) {
>
> ... handle it here
>
> if (!pages[i]) {
> nr = 1;
> continue;
> }
>
> No strong opinion. But I think we should either update all callers to deal with returning NULL from this function, and set "nr = 1".
>
Yes, that makes sense. I'll send a v2 shortly with one or the other
approach implemented. I appreciate the review feedback as always!
thanks,
--
John Hubbard
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-11-20 3:29 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-19 4:49 [PATCH] mm/gup: handle NULL pages in unpin_user_pages() John Hubbard
2024-11-19 14:33 ` David Hildenbrand
2024-11-20 3:28 ` John Hubbard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox