Re: [PATCH] mm/gup_test: fix race with PIN_LONGTERM_TEST ioctls

From: David Hildenbrand (Arm)

Date: Fri Jun 12 2026 - 03:38:11 EST


On 6/8/26 04:50, Yunhui Cui wrote:
> The PIN_LONGTERM_TEST helpers keep their state in global variables that
> are protected by pin_longterm_test_mutex when accessed from ioctl().
> However, gup_test_release() calls pin_longterm_test_stop() without
> holding that mutex.
>
> This can race with PIN_LONGTERM_TEST_STOP and let two callers operate on
> the same pages array concurrently, corrupting the test state and possibly
> freeing it twice:
>
> CPU 0 CPU 1
> ----- -----
> ioctl(PIN_LONGTERM_TEST_STOP)
> mutex_lock(&pin_longterm_test_mutex)
> pin_longterm_test_stop()
> if (pin_longterm_test_pages)
> kvfree(pin_longterm_test_pages)
>
> close()
> gup_test_release()
> pin_longterm_test_stop()
> if (pin_longterm_test_pages)
> kvfree(pin_longterm_test_pages)
>
> pin_longterm_test_pages = NULL
> mutex_unlock(&pin_longterm_test_mutex)

Okay, thinking about this some more ...

I think what's really required here is that we have two separate "struct file",
because otherwise release() cannot race with unlocked_ioctl().

Which is something we didn't expect when we added this functionality.

I think the proper way to handle this is by moving the state to the
"struct file", to actually cleanly allow concurrent usage.

So instead, I think we should do the following (untested):