* [PATCH] mm/gup_test: reject wrapped user ranges
@ 2026-06-09 0:48 Samuel Moelius
2026-06-10 1:27 ` Andrew Morton
2026-06-10 12:18 ` David Hildenbrand (Arm)
0 siblings, 2 replies; 6+ messages in thread
From: Samuel Moelius @ 2026-06-09 0:48 UTC (permalink / raw)
To: Andrew Morton
Cc: Samuel Moelius, David Hildenbrand, Jason Gunthorpe, John Hubbard,
Peter Xu, open list:MEMORY MANAGEMENT - GUP (GET USER PAGES),
open list
gup_test accepts an address and size from the debugfs ioctl and
repeatedly compares against addr + size. If that addition wraps, the
loop can be skipped and the ioctl returns success with size rewritten to
zero.
Compute the end address once with overflow checking and use that checked
end for the loop bounds.
Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com>
---
mm/gup_test.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/mm/gup_test.c b/mm/gup_test.c
index 9dd48db897b9..eb4c9cda16ed 100644
--- a/mm/gup_test.c
+++ b/mm/gup_test.c
@@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd,
unsigned long i, nr_pages, addr, next;
long nr;
struct page **pages;
+ unsigned long end;
int ret = 0;
bool needs_mmap_lock =
cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
- if (gup->size > ULONG_MAX)
+ if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX)
+ return -EINVAL;
+ if (check_add_overflow((unsigned long)gup->addr,
+ (unsigned long)gup->size, &end))
return -EINVAL;
nr_pages = gup->size / PAGE_SIZE;
@@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd,
i = 0;
nr = gup->nr_pages_per_call;
start_time = ktime_get();
- for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
+ for (addr = gup->addr; addr < end; addr = next) {
if (nr != gup->nr_pages_per_call)
break;
next = addr + nr * PAGE_SIZE;
- if (next > gup->addr + gup->size) {
- next = gup->addr + gup->size;
+ if (next > end) {
+ next = end;
nr = (next - addr) / PAGE_SIZE;
}
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mm/gup_test: reject wrapped user ranges
2026-06-09 0:48 [PATCH] mm/gup_test: reject wrapped user ranges Samuel Moelius
@ 2026-06-10 1:27 ` Andrew Morton
2026-06-10 12:21 ` David Hildenbrand (Arm)
2026-06-10 17:22 ` Samuel Moelius
2026-06-10 12:18 ` David Hildenbrand (Arm)
1 sibling, 2 replies; 6+ messages in thread
From: Andrew Morton @ 2026-06-10 1:27 UTC (permalink / raw)
To: Samuel Moelius
Cc: David Hildenbrand, Jason Gunthorpe, John Hubbard, Peter Xu,
open list:MEMORY MANAGEMENT - GUP (GET USER PAGES),
open list
On Tue, 9 Jun 2026 00:48:15 +0000 Samuel Moelius <sam.moelius@trailofbits.com> wrote:
> gup_test accepts an address and size from the debugfs ioctl and
> repeatedly compares against addr + size. If that addition wraps, the
> loop can be skipped and the ioctl returns success with size rewritten to
> zero.
>
> Compute the end address once with overflow checking and use that checked
> end for the loop bounds.
>
Looks sane, thanks.
> --- a/mm/gup_test.c
> +++ b/mm/gup_test.c
> @@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd,
> unsigned long i, nr_pages, addr, next;
> long nr;
> struct page **pages;
> + unsigned long end;
> int ret = 0;
> bool needs_mmap_lock =
> cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
>
> - if (gup->size > ULONG_MAX)
> + if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX)
> + return -EINVAL;
> + if (check_add_overflow((unsigned long)gup->addr,
> + (unsigned long)gup->size, &end))
I wonder why those fields were made __u64 instead of ulong.
> return -EINVAL;
>
> nr_pages = gup->size / PAGE_SIZE;
> @@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd,
> i = 0;
> nr = gup->nr_pages_per_call;
> start_time = ktime_get();
> - for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
> + for (addr = gup->addr; addr < end; addr = next) {
> if (nr != gup->nr_pages_per_call)
> break;
>
> next = addr + nr * PAGE_SIZE;
Sashiko AI review identified a pre-existing possible overflow here on
32-bit machines.
https://sashiko.dev/#/patchset/20260609004814.1240586.6294d614ac80.gup-test-range-end-wrap@trailofbits.com
> - if (next > gup->addr + gup->size) {
> - next = gup->addr + gup->size;
> + if (next > end) {
> + next = end;
> nr = (next - addr) / PAGE_SIZE;
> }
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mm/gup_test: reject wrapped user ranges
2026-06-10 1:27 ` Andrew Morton
@ 2026-06-10 12:21 ` David Hildenbrand (Arm)
2026-06-10 17:22 ` Samuel Moelius
1 sibling, 0 replies; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-10 12:21 UTC (permalink / raw)
To: Andrew Morton, Samuel Moelius
Cc: Jason Gunthorpe, John Hubbard, Peter Xu,
open list:MEMORY MANAGEMENT - GUP (GET USER PAGES),
open list
On 6/10/26 03:27, Andrew Morton wrote:
> On Tue, 9 Jun 2026 00:48:15 +0000 Samuel Moelius <sam.moelius@trailofbits.com> wrote:
>
>> gup_test accepts an address and size from the debugfs ioctl and
>> repeatedly compares against addr + size. If that addition wraps, the
>> loop can be skipped and the ioctl returns success with size rewritten to
>> zero.
>>
>> Compute the end address once with overflow checking and use that checked
>> end for the loop bounds.
>>
>
> Looks sane, thanks.
>
>> --- a/mm/gup_test.c
>> +++ b/mm/gup_test.c
>> @@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd,
>> unsigned long i, nr_pages, addr, next;
>> long nr;
>> struct page **pages;
>> + unsigned long end;
>> int ret = 0;
>> bool needs_mmap_lock =
>> cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
>>
>> - if (gup->size > ULONG_MAX)
>> + if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX)
>> + return -EINVAL;
>> + if (check_add_overflow((unsigned long)gup->addr,
>> + (unsigned long)gup->size, &end))
>
> I wonder why those fields were made __u64 instead of ulong.
>
>> return -EINVAL;
>>
>> nr_pages = gup->size / PAGE_SIZE;
>> @@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd,
>> i = 0;
>> nr = gup->nr_pages_per_call;
>> start_time = ktime_get();
>> - for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
>> + for (addr = gup->addr; addr < end; addr = next) {
>> if (nr != gup->nr_pages_per_call)
>> break;
>>
>> next = addr + nr * PAGE_SIZE;
>
> Sashiko AI review identified a pre-existing possible overflow here on
> 32-bit machines.
>
> https://sashiko.dev/#/patchset/20260609004814.1240586.6294d614ac80.gup-test-range-end-wrap@trailofbits.com
I mean, we don't really care for gup_test.c about any of that. The patch here
looks like a decent cleanup, so I think we should take it.
Fixing odd things on 32bit that will never happen? Not so sure.
--
Cheers,
David
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] mm/gup_test: reject wrapped user ranges
2026-06-10 1:27 ` Andrew Morton
2026-06-10 12:21 ` David Hildenbrand (Arm)
@ 2026-06-10 17:22 ` Samuel Moelius
2026-06-10 20:33 ` Andrew Morton
1 sibling, 1 reply; 6+ messages in thread
From: Samuel Moelius @ 2026-06-10 17:22 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Jason Gunthorpe, John Hubbard, Peter Xu,
open list:MEMORY MANAGEMENT - GUP (GET USER PAGES),
open list
On Tue, Jun 9, 2026 at 9:27 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Tue, 9 Jun 2026 00:48:15 +0000 Samuel Moelius <sam.moelius@trailofbits.com> wrote:
>
> > gup_test accepts an address and size from the debugfs ioctl and
> > repeatedly compares against addr + size. If that addition wraps, the
> > loop can be skipped and the ioctl returns success with size rewritten to
> > zero.
> >
> > Compute the end address once with overflow checking and use that checked
> > end for the loop bounds.
> >
>
> Looks sane, thanks.
>
> > --- a/mm/gup_test.c
> > +++ b/mm/gup_test.c
> > @@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd,
> > unsigned long i, nr_pages, addr, next;
> > long nr;
> > struct page **pages;
> > + unsigned long end;
> > int ret = 0;
> > bool needs_mmap_lock =
> > cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
> >
> > - if (gup->size > ULONG_MAX)
> > + if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX)
> > + return -EINVAL;
> > + if (check_add_overflow((unsigned long)gup->addr,
> > + (unsigned long)gup->size, &end))
>
> I wonder why those fields were made __u64 instead of ulong.
>
> > return -EINVAL;
> >
> > nr_pages = gup->size / PAGE_SIZE;
> > @@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd,
> > i = 0;
> > nr = gup->nr_pages_per_call;
> > start_time = ktime_get();
> > - for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
> > + for (addr = gup->addr; addr < end; addr = next) {
> > if (nr != gup->nr_pages_per_call)
> > break;
> >
> > next = addr + nr * PAGE_SIZE;
>
> Sashiko AI review identified a pre-existing possible overflow here on
> 32-bit machines.
>
> https://sashiko.dev/#/patchset/20260609004814.1240586.6294d614ac80.gup-test-range-end-wrap@trailofbits.com
Does the __u64 or Sashiko AI point require action from me?
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] mm/gup_test: reject wrapped user ranges
2026-06-09 0:48 [PATCH] mm/gup_test: reject wrapped user ranges Samuel Moelius
2026-06-10 1:27 ` Andrew Morton
@ 2026-06-10 12:18 ` David Hildenbrand (Arm)
1 sibling, 0 replies; 6+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-10 12:18 UTC (permalink / raw)
To: Samuel Moelius, Andrew Morton
Cc: Jason Gunthorpe, John Hubbard, Peter Xu,
open list:MEMORY MANAGEMENT - GUP (GET USER PAGES),
open list
On 6/9/26 02:48, Samuel Moelius wrote:
> gup_test accepts an address and size from the debugfs ioctl and
> repeatedly compares against addr + size. If that addition wraps, the
> loop can be skipped and the ioctl returns success with size rewritten to
> zero.
Yeah, it's only used for testing and (a) not expected to be included in
production kernels or of so (b) only accessible to root.
So we didn't particularly care about making this interface watertight.
>
> Compute the end address once with overflow checking and use that checked
> end for the loop bounds.
>
> Assisted-by: Codex:gpt-5.5-cyber-preview
> Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com>
> ---
> mm/gup_test.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/mm/gup_test.c b/mm/gup_test.c
> index 9dd48db897b9..eb4c9cda16ed 100644
> --- a/mm/gup_test.c
> +++ b/mm/gup_test.c
> @@ -105,11 +105,15 @@ static int __gup_test_ioctl(unsigned int cmd,
> unsigned long i, nr_pages, addr, next;
> long nr;
> struct page **pages;
> + unsigned long end;
> int ret = 0;
> bool needs_mmap_lock =
> cmd != GUP_FAST_BENCHMARK && cmd != PIN_FAST_BENCHMARK;
>
> - if (gup->size > ULONG_MAX)
> + if (gup->addr > ULONG_MAX || gup->size > ULONG_MAX)
> + return -EINVAL;
> + if (check_add_overflow((unsigned long)gup->addr,
> + (unsigned long)gup->size, &end))
> return -EINVAL;
>
> nr_pages = gup->size / PAGE_SIZE;
> @@ -125,13 +129,13 @@ static int __gup_test_ioctl(unsigned int cmd,
> i = 0;
> nr = gup->nr_pages_per_call;
> start_time = ktime_get();
> - for (addr = gup->addr; addr < gup->addr + gup->size; addr = next) {
> + for (addr = gup->addr; addr < end; addr = next) {
> if (nr != gup->nr_pages_per_call)
> break;
>
> next = addr + nr * PAGE_SIZE;
> - if (next > gup->addr + gup->size) {
> - next = gup->addr + gup->size;
> + if (next > end) {
> + next = end;
> nr = (next - addr) / PAGE_SIZE;
> }
>
LGTM
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-10 20:33 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-09 0:48 [PATCH] mm/gup_test: reject wrapped user ranges Samuel Moelius
2026-06-10 1:27 ` Andrew Morton
2026-06-10 12:21 ` David Hildenbrand (Arm)
2026-06-10 17:22 ` Samuel Moelius
2026-06-10 20:33 ` Andrew Morton
2026-06-10 12:18 ` David Hildenbrand (Arm)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®