linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
@ 2025-08-05 17:51 Zi Yan
  2025-08-05 18:38 ` Jann Horn
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Zi Yan @ 2025-08-05 17:51 UTC (permalink / raw)
  To: wang lian, Andrew Morton, linux-mm
  Cc: Lorenzo Stoakes, David Hildenbrand, Wei Yang, Christian Brauner,
	Jann Horn, Kairui Song, Liam Howlett, Mark Brown, SeongJae Park,
	Shuah Khan, Vlastimil Babka, linux-kernel, linux-kselftest,
	Zi Yan

FORCE_READ() converts input value x to its pointer type then reads from
address x. This is wrong. If x is a non-pointer, it would be caught it
easily. But all FORCE_READ() callers are trying to read from a pointer and
FORCE_READ() basically reads a pointer to a pointer instead of the original
typed pointer. Almost no access violation was found, except the one from
split_huge_page_test.

Fix it by implementing a simplified READ_ONCE() instead.

Fixes: 3f6bfd4789a0 ("selftests/mm: reuse FORCE_READ to replace "asm volatile("" : "+r" (XXX));"")
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
FORCE_READ() comes from commit 876320d71f51 ("selftests/mm: add self tests for
guard page feature"). I will a separate patch to stable tree.


 tools/testing/selftests/mm/cow.c                  | 4 ++--
 tools/testing/selftests/mm/guard-regions.c        | 2 +-
 tools/testing/selftests/mm/hugetlb-madvise.c      | 4 +++-
 tools/testing/selftests/mm/migration.c            | 2 +-
 tools/testing/selftests/mm/pagemap_ioctl.c        | 2 +-
 tools/testing/selftests/mm/split_huge_page_test.c | 7 +++++--
 tools/testing/selftests/mm/vm_util.h              | 2 +-
 7 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index d30625c18259..c744c603d688 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -1554,8 +1554,8 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc)
 	}
 
 	/* Read from the page to populate the shared zeropage. */
-	FORCE_READ(mem);
-	FORCE_READ(smem);
+	FORCE_READ(*mem);
+	FORCE_READ(*smem);
 
 	fn(mem, smem, pagesize);
 munmap:
diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
index b0d42eb04e3a..8dd81c0a4a5a 100644
--- a/tools/testing/selftests/mm/guard-regions.c
+++ b/tools/testing/selftests/mm/guard-regions.c
@@ -145,7 +145,7 @@ static bool try_access_buf(char *ptr, bool write)
 		if (write)
 			*ptr = 'x';
 		else
-			FORCE_READ(ptr);
+			FORCE_READ(*ptr);
 	}
 
 	signal_jump_set = false;
diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
index 1afe14b9dc0c..c5940c0595be 100644
--- a/tools/testing/selftests/mm/hugetlb-madvise.c
+++ b/tools/testing/selftests/mm/hugetlb-madvise.c
@@ -50,8 +50,10 @@ void read_fault_pages(void *addr, unsigned long nr_pages)
 	unsigned long i;
 
 	for (i = 0; i < nr_pages; i++) {
+		unsigned long *addr2 =
+			((unsigned long *)(addr + (i * huge_page_size)));
 		/* Prevent the compiler from optimizing out the entire loop: */
-		FORCE_READ(((unsigned long *)(addr + (i * huge_page_size))));
+		FORCE_READ(*addr2);
 	}
 }
 
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index c5a73617796a..ea945eebec2f 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -110,7 +110,7 @@ void *access_mem(void *ptr)
 		 * the memory access actually happens and prevents the compiler
 		 * from optimizing away this entire loop.
 		 */
-		FORCE_READ((uint64_t *)ptr);
+		FORCE_READ(*(uint64_t *)ptr);
 	}
 
 	return NULL;
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
index 0d4209eef0c3..e6face7c0166 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -1525,7 +1525,7 @@ void zeropfn_tests(void)
 
 	ret = madvise(mem, hpage_size, MADV_HUGEPAGE);
 	if (!ret) {
-		FORCE_READ(mem);
+		FORCE_READ(*mem);
 
 		ret = pagemap_ioctl(mem, hpage_size, &vec, 1, 0,
 				    0, PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO);
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 718daceb5282..3c761228e451 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -440,8 +440,11 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd,
 	}
 	madvise(*addr, fd_size, MADV_HUGEPAGE);
 
-	for (size_t i = 0; i < fd_size; i++)
-		FORCE_READ((*addr + i));
+	for (size_t i = 0; i < fd_size; i++) {
+		char *addr2 = *addr + i;
+
+		FORCE_READ(*addr2);
+	}
 
 	if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) {
 		ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n");
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index c20298ae98ea..b55d1809debc 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -23,7 +23,7 @@
  * anything with it in order to trigger a read page fault. We therefore must use
  * volatile to stop the compiler from optimising this away.
  */
-#define FORCE_READ(x) (*(volatile typeof(x) *)x)
+#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
 
 extern unsigned int __page_size;
 extern unsigned int __page_shift;
-- 
2.47.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 17:51 [PATCH] selftests/mm: fix FORCE_READ to read input value correctly Zi Yan
@ 2025-08-05 18:38 ` Jann Horn
  2025-08-05 18:48   ` Zi Yan
  2025-08-05 19:00 ` Lorenzo Stoakes
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Jann Horn @ 2025-08-05 18:38 UTC (permalink / raw)
  To: Zi Yan
  Cc: wang lian, Andrew Morton, linux-mm, Lorenzo Stoakes,
	David Hildenbrand, Wei Yang, Christian Brauner, Kairui Song,
	Liam Howlett, Mark Brown, SeongJae Park, Shuah Khan,
	Vlastimil Babka, linux-kernel, linux-kselftest

On Tue, Aug 5, 2025 at 7:51 PM Zi Yan <ziy@nvidia.com> wrote:
> FORCE_READ() converts input value x to its pointer type then reads from
> address x. This is wrong. If x is a non-pointer, it would be caught it
> easily. But all FORCE_READ() callers are trying to read from a pointer and
> FORCE_READ() basically reads a pointer to a pointer instead of the original
> typed pointer. Almost no access violation was found, except the one from
> split_huge_page_test.
[...]
> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> index c20298ae98ea..b55d1809debc 100644
> --- a/tools/testing/selftests/mm/vm_util.h
> +++ b/tools/testing/selftests/mm/vm_util.h
> @@ -23,7 +23,7 @@
>   * anything with it in order to trigger a read page fault. We therefore must use
>   * volatile to stop the compiler from optimising this away.
>   */
> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))

So is the problem with the old code basically that it should have been
something like

#define FORCE_READ(x) (*(volatile typeof(*(x)) *)(x))

to actually cast the normal pointer to a volatile pointer?


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 18:38 ` Jann Horn
@ 2025-08-05 18:48   ` Zi Yan
  2025-08-05 18:56     ` Lorenzo Stoakes
  0 siblings, 1 reply; 12+ messages in thread
From: Zi Yan @ 2025-08-05 18:48 UTC (permalink / raw)
  To: Jann Horn
  Cc: wang lian, Andrew Morton, linux-mm, Lorenzo Stoakes,
	David Hildenbrand, Wei Yang, Christian Brauner, Kairui Song,
	Liam Howlett, Mark Brown, SeongJae Park, Shuah Khan,
	Vlastimil Babka, linux-kernel, linux-kselftest

On 5 Aug 2025, at 14:38, Jann Horn wrote:

> On Tue, Aug 5, 2025 at 7:51 PM Zi Yan <ziy@nvidia.com> wrote:
>> FORCE_READ() converts input value x to its pointer type then reads from
>> address x. This is wrong. If x is a non-pointer, it would be caught it
>> easily. But all FORCE_READ() callers are trying to read from a pointer and
>> FORCE_READ() basically reads a pointer to a pointer instead of the original
>> typed pointer. Almost no access violation was found, except the one from
>> split_huge_page_test.
> [...]
>> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
>> index c20298ae98ea..b55d1809debc 100644
>> --- a/tools/testing/selftests/mm/vm_util.h
>> +++ b/tools/testing/selftests/mm/vm_util.h
>> @@ -23,7 +23,7 @@
>>   * anything with it in order to trigger a read page fault. We therefore must use
>>   * volatile to stop the compiler from optimising this away.
>>   */
>> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
>> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
>
> So is the problem with the old code basically that it should have been
> something like
>
> #define FORCE_READ(x) (*(volatile typeof(*(x)) *)(x))
>
> to actually cast the normal pointer to a volatile pointer?

Yeah. That works too. I would rename it to FORCE_READ_PTR to avoid
misuse. :)

Best Regards,
Yan, Zi


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 18:48   ` Zi Yan
@ 2025-08-05 18:56     ` Lorenzo Stoakes
  0 siblings, 0 replies; 12+ messages in thread
From: Lorenzo Stoakes @ 2025-08-05 18:56 UTC (permalink / raw)
  To: Zi Yan
  Cc: Jann Horn, wang lian, Andrew Morton, linux-mm, David Hildenbrand,
	Wei Yang, Christian Brauner, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest

On Tue, Aug 05, 2025 at 02:48:25PM -0400, Zi Yan wrote:
> On 5 Aug 2025, at 14:38, Jann Horn wrote:
>
> > On Tue, Aug 5, 2025 at 7:51 PM Zi Yan <ziy@nvidia.com> wrote:
> >> FORCE_READ() converts input value x to its pointer type then reads from
> >> address x. This is wrong. If x is a non-pointer, it would be caught it
> >> easily. But all FORCE_READ() callers are trying to read from a pointer and
> >> FORCE_READ() basically reads a pointer to a pointer instead of the original
> >> typed pointer. Almost no access violation was found, except the one from
> >> split_huge_page_test.
> > [...]
> >> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> >> index c20298ae98ea..b55d1809debc 100644
> >> --- a/tools/testing/selftests/mm/vm_util.h
> >> +++ b/tools/testing/selftests/mm/vm_util.h
> >> @@ -23,7 +23,7 @@
> >>   * anything with it in order to trigger a read page fault. We therefore must use
> >>   * volatile to stop the compiler from optimising this away.
> >>   */
> >> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> >> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
> >
> > So is the problem with the old code basically that it should have been
> > something like
> >
> > #define FORCE_READ(x) (*(volatile typeof(*(x)) *)(x))
> >
> > to actually cast the normal pointer to a volatile pointer?
>
> Yeah. That works too. I would rename it to FORCE_READ_PTR to avoid
> misuse. :)

We were having this discussion on IRC, generally I'm fine with it as-is,
though my version sort of intended to make life easier by passing a ptr,
but this version makes it closer to READ_ONCE() so probably semantically a
little better. :)

>
> Best Regards,
> Yan, Zi


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 17:51 [PATCH] selftests/mm: fix FORCE_READ to read input value correctly Zi Yan
  2025-08-05 18:38 ` Jann Horn
@ 2025-08-05 19:00 ` Lorenzo Stoakes
  2025-08-05 19:09   ` Zi Yan
  2025-08-05 21:05 ` David Hildenbrand
  2025-08-06  2:07 ` Wei Yang
  3 siblings, 1 reply; 12+ messages in thread
From: Lorenzo Stoakes @ 2025-08-05 19:00 UTC (permalink / raw)
  To: Zi Yan
  Cc: wang lian, Andrew Morton, linux-mm, David Hildenbrand, Wei Yang,
	Christian Brauner, Jann Horn, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest

On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
> FORCE_READ() converts input value x to its pointer type then reads from
> address x. This is wrong. If x is a non-pointer, it would be caught it
> easily. But all FORCE_READ() callers are trying to read from a pointer and
> FORCE_READ() basically reads a pointer to a pointer instead of the original
> typed pointer. Almost no access violation was found, except the one from
> split_huge_page_test.

Oops, sorry about that! I had incorrectly assumed typeof() decayed the type
when I wrote the guard-regions test code, and hadn't considered that we
were casting to (t **) and dereffing that.

And as discussed off-list, if you deref a char array like that, and are at
the end of an array, that's err... not brilliant :)

>
> Fix it by implementing a simplified READ_ONCE() instead.

I sort of intended to make this easier for pointers, but the semantics of
this are actually potentially a bit nicer - it's more like READ_ONCE() and
you're passing in the value you're actually reading so this is probably
better.

>
> Fixes: 3f6bfd4789a0 ("selftests/mm: reuse FORCE_READ to replace "asm volatile("" : "+r" (XXX));"")
> Signed-off-by: Zi Yan <ziy@nvidia.com>

LGTM, so:

Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>

But see nits below.

> ---
> FORCE_READ() comes from commit 876320d71f51 ("selftests/mm: add self tests for
> guard page feature"). I will a separate patch to stable tree.
>
>
>  tools/testing/selftests/mm/cow.c                  | 4 ++--
>  tools/testing/selftests/mm/guard-regions.c        | 2 +-
>  tools/testing/selftests/mm/hugetlb-madvise.c      | 4 +++-
>  tools/testing/selftests/mm/migration.c            | 2 +-
>  tools/testing/selftests/mm/pagemap_ioctl.c        | 2 +-
>  tools/testing/selftests/mm/split_huge_page_test.c | 7 +++++--
>  tools/testing/selftests/mm/vm_util.h              | 2 +-
>  7 files changed, 14 insertions(+), 9 deletions(-)
>
> diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
> index d30625c18259..c744c603d688 100644
> --- a/tools/testing/selftests/mm/cow.c
> +++ b/tools/testing/selftests/mm/cow.c
> @@ -1554,8 +1554,8 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc)
>  	}
>
>  	/* Read from the page to populate the shared zeropage. */
> -	FORCE_READ(mem);
> -	FORCE_READ(smem);
> +	FORCE_READ(*mem);
> +	FORCE_READ(*smem);
>
>  	fn(mem, smem, pagesize);
>  munmap:
> diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
> index b0d42eb04e3a..8dd81c0a4a5a 100644
> --- a/tools/testing/selftests/mm/guard-regions.c
> +++ b/tools/testing/selftests/mm/guard-regions.c
> @@ -145,7 +145,7 @@ static bool try_access_buf(char *ptr, bool write)
>  		if (write)
>  			*ptr = 'x';
>  		else
> -			FORCE_READ(ptr);
> +			FORCE_READ(*ptr);
>  	}
>
>  	signal_jump_set = false;
> diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
> index 1afe14b9dc0c..c5940c0595be 100644
> --- a/tools/testing/selftests/mm/hugetlb-madvise.c
> +++ b/tools/testing/selftests/mm/hugetlb-madvise.c
> @@ -50,8 +50,10 @@ void read_fault_pages(void *addr, unsigned long nr_pages)
>  	unsigned long i;
>
>  	for (i = 0; i < nr_pages; i++) {
> +		unsigned long *addr2 =
> +			((unsigned long *)(addr + (i * huge_page_size)));
>  		/* Prevent the compiler from optimizing out the entire loop: */
> -		FORCE_READ(((unsigned long *)(addr + (i * huge_page_size))));
> +		FORCE_READ(*addr2);
>  	}
>  }
>
> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
> index c5a73617796a..ea945eebec2f 100644
> --- a/tools/testing/selftests/mm/migration.c
> +++ b/tools/testing/selftests/mm/migration.c
> @@ -110,7 +110,7 @@ void *access_mem(void *ptr)
>  		 * the memory access actually happens and prevents the compiler
>  		 * from optimizing away this entire loop.
>  		 */
> -		FORCE_READ((uint64_t *)ptr);
> +		FORCE_READ(*(uint64_t *)ptr);
>  	}
>
>  	return NULL;
> diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
> index 0d4209eef0c3..e6face7c0166 100644
> --- a/tools/testing/selftests/mm/pagemap_ioctl.c
> +++ b/tools/testing/selftests/mm/pagemap_ioctl.c
> @@ -1525,7 +1525,7 @@ void zeropfn_tests(void)
>
>  	ret = madvise(mem, hpage_size, MADV_HUGEPAGE);
>  	if (!ret) {
> -		FORCE_READ(mem);
> +		FORCE_READ(*mem);
>
>  		ret = pagemap_ioctl(mem, hpage_size, &vec, 1, 0,
>  				    0, PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO);
> diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
> index 718daceb5282..3c761228e451 100644
> --- a/tools/testing/selftests/mm/split_huge_page_test.c
> +++ b/tools/testing/selftests/mm/split_huge_page_test.c
> @@ -440,8 +440,11 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd,
>  	}
>  	madvise(*addr, fd_size, MADV_HUGEPAGE);
>
> -	for (size_t i = 0; i < fd_size; i++)
> -		FORCE_READ((*addr + i));
> +	for (size_t i = 0; i < fd_size; i++) {
> +		char *addr2 = *addr + i;
> +
> +		FORCE_READ(*addr2);
> +	}
>
>  	if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) {
>  		ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n");
> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> index c20298ae98ea..b55d1809debc 100644
> --- a/tools/testing/selftests/mm/vm_util.h
> +++ b/tools/testing/selftests/mm/vm_util.h
> @@ -23,7 +23,7 @@
>   * anything with it in order to trigger a read page fault. We therefore must use
>   * volatile to stop the compiler from optimising this away.
>   */
> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))

NIT: but wonder if const is necessary, and also (as discussed off-list
again :) will this work with a (void) prefixed, just to a. make it clear
we're reading but discarding and b. to avoid any possible compiler warning
on this?

I know for some reason this form doesn't generate one currently (not sure
why), but we may hit that in future.

>
>  extern unsigned int __page_size;
>  extern unsigned int __page_shift;
> --
> 2.47.2
>

Cheers, Lorenzo


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 19:00 ` Lorenzo Stoakes
@ 2025-08-05 19:09   ` Zi Yan
  2025-08-05 19:21     ` Lorenzo Stoakes
  2025-08-06  1:44     ` wang lian
  0 siblings, 2 replies; 12+ messages in thread
From: Zi Yan @ 2025-08-05 19:09 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: wang lian, Andrew Morton, linux-mm, David Hildenbrand, Wei Yang,
	Christian Brauner, Jann Horn, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest

On 5 Aug 2025, at 15:00, Lorenzo Stoakes wrote:

> On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
>> FORCE_READ() converts input value x to its pointer type then reads from
>> address x. This is wrong. If x is a non-pointer, it would be caught it
>> easily. But all FORCE_READ() callers are trying to read from a pointer and
>> FORCE_READ() basically reads a pointer to a pointer instead of the original
>> typed pointer. Almost no access violation was found, except the one from
>> split_huge_page_test.
>
> Oops, sorry about that! I had incorrectly assumed typeof() decayed the type
> when I wrote the guard-regions test code, and hadn't considered that we
> were casting to (t **) and dereffing that.
>
> And as discussed off-list, if you deref a char array like that, and are at
> the end of an array, that's err... not brilliant :)
>
>>
>> Fix it by implementing a simplified READ_ONCE() instead.
>
> I sort of intended to make this easier for pointers, but the semantics of
> this are actually potentially a bit nicer - it's more like READ_ONCE() and
> you're passing in the value you're actually reading so this is probably
> better.
>
>>
>> Fixes: 3f6bfd4789a0 ("selftests/mm: reuse FORCE_READ to replace "asm volatile("" : "+r" (XXX));"")
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>
> LGTM, so:
>
> Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
>
> But see nits below.
>
>> ---
>> FORCE_READ() comes from commit 876320d71f51 ("selftests/mm: add self tests for
>> guard page feature"). I will a separate patch to stable tree.
>>
>>
>>  tools/testing/selftests/mm/cow.c                  | 4 ++--
>>  tools/testing/selftests/mm/guard-regions.c        | 2 +-
>>  tools/testing/selftests/mm/hugetlb-madvise.c      | 4 +++-
>>  tools/testing/selftests/mm/migration.c            | 2 +-
>>  tools/testing/selftests/mm/pagemap_ioctl.c        | 2 +-
>>  tools/testing/selftests/mm/split_huge_page_test.c | 7 +++++--
>>  tools/testing/selftests/mm/vm_util.h              | 2 +-
>>  7 files changed, 14 insertions(+), 9 deletions(-)
>>
>> diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
>> index d30625c18259..c744c603d688 100644
>> --- a/tools/testing/selftests/mm/cow.c
>> +++ b/tools/testing/selftests/mm/cow.c
>> @@ -1554,8 +1554,8 @@ static void run_with_zeropage(non_anon_test_fn fn, const char *desc)
>>  	}
>>
>>  	/* Read from the page to populate the shared zeropage. */
>> -	FORCE_READ(mem);
>> -	FORCE_READ(smem);
>> +	FORCE_READ(*mem);
>> +	FORCE_READ(*smem);
>>
>>  	fn(mem, smem, pagesize);
>>  munmap:
>> diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
>> index b0d42eb04e3a..8dd81c0a4a5a 100644
>> --- a/tools/testing/selftests/mm/guard-regions.c
>> +++ b/tools/testing/selftests/mm/guard-regions.c
>> @@ -145,7 +145,7 @@ static bool try_access_buf(char *ptr, bool write)
>>  		if (write)
>>  			*ptr = 'x';
>>  		else
>> -			FORCE_READ(ptr);
>> +			FORCE_READ(*ptr);
>>  	}
>>
>>  	signal_jump_set = false;
>> diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
>> index 1afe14b9dc0c..c5940c0595be 100644
>> --- a/tools/testing/selftests/mm/hugetlb-madvise.c
>> +++ b/tools/testing/selftests/mm/hugetlb-madvise.c
>> @@ -50,8 +50,10 @@ void read_fault_pages(void *addr, unsigned long nr_pages)
>>  	unsigned long i;
>>
>>  	for (i = 0; i < nr_pages; i++) {
>> +		unsigned long *addr2 =
>> +			((unsigned long *)(addr + (i * huge_page_size)));
>>  		/* Prevent the compiler from optimizing out the entire loop: */
>> -		FORCE_READ(((unsigned long *)(addr + (i * huge_page_size))));
>> +		FORCE_READ(*addr2);
>>  	}
>>  }
>>
>> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
>> index c5a73617796a..ea945eebec2f 100644
>> --- a/tools/testing/selftests/mm/migration.c
>> +++ b/tools/testing/selftests/mm/migration.c
>> @@ -110,7 +110,7 @@ void *access_mem(void *ptr)
>>  		 * the memory access actually happens and prevents the compiler
>>  		 * from optimizing away this entire loop.
>>  		 */
>> -		FORCE_READ((uint64_t *)ptr);
>> +		FORCE_READ(*(uint64_t *)ptr);
>>  	}
>>
>>  	return NULL;
>> diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
>> index 0d4209eef0c3..e6face7c0166 100644
>> --- a/tools/testing/selftests/mm/pagemap_ioctl.c
>> +++ b/tools/testing/selftests/mm/pagemap_ioctl.c
>> @@ -1525,7 +1525,7 @@ void zeropfn_tests(void)
>>
>>  	ret = madvise(mem, hpage_size, MADV_HUGEPAGE);
>>  	if (!ret) {
>> -		FORCE_READ(mem);
>> +		FORCE_READ(*mem);
>>
>>  		ret = pagemap_ioctl(mem, hpage_size, &vec, 1, 0,
>>  				    0, PAGE_IS_PFNZERO, 0, 0, PAGE_IS_PFNZERO);
>> diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
>> index 718daceb5282..3c761228e451 100644
>> --- a/tools/testing/selftests/mm/split_huge_page_test.c
>> +++ b/tools/testing/selftests/mm/split_huge_page_test.c
>> @@ -440,8 +440,11 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd,
>>  	}
>>  	madvise(*addr, fd_size, MADV_HUGEPAGE);
>>
>> -	for (size_t i = 0; i < fd_size; i++)
>> -		FORCE_READ((*addr + i));
>> +	for (size_t i = 0; i < fd_size; i++) {
>> +		char *addr2 = *addr + i;
>> +
>> +		FORCE_READ(*addr2);
>> +	}
>>
>>  	if (!check_huge_file(*addr, fd_size / pmd_pagesize, pmd_pagesize)) {
>>  		ksft_print_msg("No large pagecache folio generated, please provide a filesystem supporting large folio\n");
>> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
>> index c20298ae98ea..b55d1809debc 100644
>> --- a/tools/testing/selftests/mm/vm_util.h
>> +++ b/tools/testing/selftests/mm/vm_util.h
>> @@ -23,7 +23,7 @@
>>   * anything with it in order to trigger a read page fault. We therefore must use
>>   * volatile to stop the compiler from optimising this away.
>>   */
>> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
>> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
>
> NIT: but wonder if const is necessary, and also (as discussed off-list

I just used READ_ONCE() code, but it is not necessary.

> again :) will this work with a (void) prefixed, just to a. make it clear
> we're reading but discarding and b. to avoid any possible compiler warning
> on this?

Adding (void) makes no difference, at least from godbolt.

>
> I know for some reason this form doesn't generate one currently (not sure
> why), but we may hit that in future.

Neither gcc nor clang complains without (void). My guess is that volatile
is doing something there.

Best Regards,
Yan, Zi


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 19:09   ` Zi Yan
@ 2025-08-05 19:21     ` Lorenzo Stoakes
  2025-08-06 10:34       ` Pedro Falcato
  2025-08-06  1:44     ` wang lian
  1 sibling, 1 reply; 12+ messages in thread
From: Lorenzo Stoakes @ 2025-08-05 19:21 UTC (permalink / raw)
  To: Zi Yan
  Cc: wang lian, Andrew Morton, linux-mm, David Hildenbrand, Wei Yang,
	Christian Brauner, Jann Horn, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest, Pedro Falcato

+cc Pedro

On Tue, Aug 05, 2025 at 03:09:54PM -0400, Zi Yan wrote:
> On 5 Aug 2025, at 15:00, Lorenzo Stoakes wrote:
>
> > On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
> >> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> >> index c20298ae98ea..b55d1809debc 100644
> >> --- a/tools/testing/selftests/mm/vm_util.h
> >> +++ b/tools/testing/selftests/mm/vm_util.h
> >> @@ -23,7 +23,7 @@
> >>   * anything with it in order to trigger a read page fault. We therefore must use
> >>   * volatile to stop the compiler from optimising this away.
> >>   */
> >> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> >> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
> >
> > NIT: but wonder if const is necessary, and also (as discussed off-list
>
> I just used READ_ONCE() code, but it is not necessary.

It's not end of the world though.

>
> > again :) will this work with a (void) prefixed, just to a. make it clear
> > we're reading but discarding and b. to avoid any possible compiler warning
> > on this?
>
> Adding (void) makes no difference, at least from godbolt.

Yeah I won't pretend to understand _exactly_ what the compiler is doing here, if
this is working in practice across multiple compilers and read-faulting the page
that's good enough for me :)

>
> >
> > I know for some reason this form doesn't generate one currently (not sure
> > why), but we may hit that in future.
>
> Neither gcc nor clang complains without (void). My guess is that volatile
> is doing something there.

Indeed possibly, be interesting if you or Pedro who's also playing with this
could nail down exactly what's going on here.

>
> Best Regards,
> Yan, Zi

But from my point of view this patch is fine - ship it! :)

Cheers, Lorenzo


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 17:51 [PATCH] selftests/mm: fix FORCE_READ to read input value correctly Zi Yan
  2025-08-05 18:38 ` Jann Horn
  2025-08-05 19:00 ` Lorenzo Stoakes
@ 2025-08-05 21:05 ` David Hildenbrand
  2025-08-06  2:07 ` Wei Yang
  3 siblings, 0 replies; 12+ messages in thread
From: David Hildenbrand @ 2025-08-05 21:05 UTC (permalink / raw)
  To: Zi Yan, wang lian, Andrew Morton, linux-mm
  Cc: Lorenzo Stoakes, Wei Yang, Christian Brauner, Jann Horn,
	Kairui Song, Liam Howlett, Mark Brown, SeongJae Park, Shuah Khan,
	Vlastimil Babka, linux-kernel, linux-kselftest

On 05.08.25 19:51, Zi Yan wrote:
> FORCE_READ() converts input value x to its pointer type then reads from
> address x. This is wrong. If x is a non-pointer, it would be caught it
> easily. But all FORCE_READ() callers are trying to read from a pointer and
> FORCE_READ() basically reads a pointer to a pointer instead of the original
> typed pointer. Almost no access violation was found, except the one from
> split_huge_page_test.
> 
> Fix it by implementing a simplified READ_ONCE() instead.
> 
> Fixes: 3f6bfd4789a0 ("selftests/mm: reuse FORCE_READ to replace "asm volatile("" : "+r" (XXX));"")
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---

Ouch, thank!

Acked-by: David Hildenbrand <david@redhat.com>

-- 
Cheers,

David / dhildenb



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 19:09   ` Zi Yan
  2025-08-05 19:21     ` Lorenzo Stoakes
@ 2025-08-06  1:44     ` wang lian
  1 sibling, 0 replies; 12+ messages in thread
From: wang lian @ 2025-08-06  1:44 UTC (permalink / raw)
  To: ziy
  Cc: akpm, brauner, broonie, david, jannh, liam.howlett, lianux.mm,
	linux-kernel, linux-kselftest, linux-mm, lorenzo.stoakes,
	richard.weiyang, ryncsn, shuah, sj, vbabka


Hi Zi Yan, Lorenzo,

Thank you for the detailed discussion. I have been following the
thread closely and it has been very insightful.

Zi Yan's fix is excellent and I appreciate the rigorous analysis.
Lorenzo's feedback has also deepened my own understanding of the
subtleties around the FORCE_READ macro.

Out of curiosity, I also checked the `(void)` prefixing on Godbolt.
As Zi Yan concluded, the resulting assembly appears identical.

I will be happy to join any future discussions regarding the exact
behavior of volatile in this context.

For this patch, it's definitely LGTM from my side as well, so.
Reviewed-by:wang lian <lianux.mm@gmail.com>


Thanks,
wang lian



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 17:51 [PATCH] selftests/mm: fix FORCE_READ to read input value correctly Zi Yan
                   ` (2 preceding siblings ...)
  2025-08-05 21:05 ` David Hildenbrand
@ 2025-08-06  2:07 ` Wei Yang
  3 siblings, 0 replies; 12+ messages in thread
From: Wei Yang @ 2025-08-06  2:07 UTC (permalink / raw)
  To: Zi Yan
  Cc: wang lian, Andrew Morton, linux-mm, Lorenzo Stoakes,
	David Hildenbrand, Wei Yang, Christian Brauner, Jann Horn,
	Kairui Song, Liam Howlett, Mark Brown, SeongJae Park, Shuah Khan,
	Vlastimil Babka, linux-kernel, linux-kselftest

On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
>FORCE_READ() converts input value x to its pointer type then reads from
>address x. This is wrong. If x is a non-pointer, it would be caught it
>easily. But all FORCE_READ() callers are trying to read from a pointer and
>FORCE_READ() basically reads a pointer to a pointer instead of the original
>typed pointer. Almost no access violation was found, except the one from
>split_huge_page_test.
>
>Fix it by implementing a simplified READ_ONCE() instead.
>
>Fixes: 3f6bfd4789a0 ("selftests/mm: reuse FORCE_READ to replace "asm volatile("" : "+r" (XXX));"")
>Signed-off-by: Zi Yan <ziy@nvidia.com>
>---

Reviewed-by: Wei Yang <richard.weiyang@gmail.com>

-- 
Wei Yang
Help you, Help me


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-05 19:21     ` Lorenzo Stoakes
@ 2025-08-06 10:34       ` Pedro Falcato
  2025-08-06 10:37         ` Lorenzo Stoakes
  0 siblings, 1 reply; 12+ messages in thread
From: Pedro Falcato @ 2025-08-06 10:34 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: Zi Yan, wang lian, Andrew Morton, linux-mm, David Hildenbrand,
	Wei Yang, Christian Brauner, Jann Horn, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest

On Tue, Aug 05, 2025 at 08:21:23PM +0100, Lorenzo Stoakes wrote:
> +cc Pedro
> 
> On Tue, Aug 05, 2025 at 03:09:54PM -0400, Zi Yan wrote:
> > On 5 Aug 2025, at 15:00, Lorenzo Stoakes wrote:
> >
> > > On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
> > >> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> > >> index c20298ae98ea..b55d1809debc 100644
> > >> --- a/tools/testing/selftests/mm/vm_util.h
> > >> +++ b/tools/testing/selftests/mm/vm_util.h
> > >> @@ -23,7 +23,7 @@
> > >>   * anything with it in order to trigger a read page fault. We therefore must use
> > >>   * volatile to stop the compiler from optimising this away.
> > >>   */
> > >> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> > >> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
> > >
> > > NIT: but wonder if const is necessary, and also (as discussed off-list
> >
> > I just used READ_ONCE() code, but it is not necessary.
> 
> It's not end of the world though.
> 
> >
> > > again :) will this work with a (void) prefixed, just to a. make it clear
> > > we're reading but discarding and b. to avoid any possible compiler warning
> > > on this?
> >
> > Adding (void) makes no difference, at least from godbolt.
>

I disagree with adding (void), because volatile being properly propagated into
the type should hide any Wunused-value warnings (because volatile reads can have
side effects, so discarding a read is most definitely valid).

And as I was seeing in https://godbolt.org/z/jnWsET1vx yesterday, GCC (and clang)
can silently drop the volatile qualifier For Some Reason.

-- 
Pedro


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] selftests/mm: fix FORCE_READ to read input value correctly.
  2025-08-06 10:34       ` Pedro Falcato
@ 2025-08-06 10:37         ` Lorenzo Stoakes
  0 siblings, 0 replies; 12+ messages in thread
From: Lorenzo Stoakes @ 2025-08-06 10:37 UTC (permalink / raw)
  To: Pedro Falcato
  Cc: Zi Yan, wang lian, Andrew Morton, linux-mm, David Hildenbrand,
	Wei Yang, Christian Brauner, Jann Horn, Kairui Song, Liam Howlett,
	Mark Brown, SeongJae Park, Shuah Khan, Vlastimil Babka,
	linux-kernel, linux-kselftest

On Wed, Aug 06, 2025 at 11:34:57AM +0100, Pedro Falcato wrote:
> On Tue, Aug 05, 2025 at 08:21:23PM +0100, Lorenzo Stoakes wrote:
> > +cc Pedro
> >
> > On Tue, Aug 05, 2025 at 03:09:54PM -0400, Zi Yan wrote:
> > > On 5 Aug 2025, at 15:00, Lorenzo Stoakes wrote:
> > >
> > > > On Tue, Aug 05, 2025 at 01:51:40PM -0400, Zi Yan wrote:
> > > >> diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
> > > >> index c20298ae98ea..b55d1809debc 100644
> > > >> --- a/tools/testing/selftests/mm/vm_util.h
> > > >> +++ b/tools/testing/selftests/mm/vm_util.h
> > > >> @@ -23,7 +23,7 @@
> > > >>   * anything with it in order to trigger a read page fault. We therefore must use
> > > >>   * volatile to stop the compiler from optimising this away.
> > > >>   */
> > > >> -#define FORCE_READ(x) (*(volatile typeof(x) *)x)
> > > >> +#define FORCE_READ(x) (*(const volatile typeof(x) *)&(x))
> > > >
> > > > NIT: but wonder if const is necessary, and also (as discussed off-list
> > >
> > > I just used READ_ONCE() code, but it is not necessary.
> >
> > It's not end of the world though.
> >
> > >
> > > > again :) will this work with a (void) prefixed, just to a. make it clear
> > > > we're reading but discarding and b. to avoid any possible compiler warning
> > > > on this?
> > >
> > > Adding (void) makes no difference, at least from godbolt.
> >
>
> I disagree with adding (void), because volatile being properly propagated into
> the type should hide any Wunused-value warnings (because volatile reads can have
> side effects, so discarding a read is most definitely valid).

Yeah, I just wondered _why_.

I mean this is fine as-is. I believe Andrew's already taken the patch as a
hotfix anyway.

>
> And as I was seeing in https://godbolt.org/z/jnWsET1vx yesterday, GCC (and clang)
> can silently drop the volatile qualifier For Some Reason.

Ack, would love to know why, but don't have the time to explore so was hoping
you/someone else could figure it out and tell me :P

>
> --
> Pedro

Cheers, Lorenzo


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-08-06 10:37 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-05 17:51 [PATCH] selftests/mm: fix FORCE_READ to read input value correctly Zi Yan
2025-08-05 18:38 ` Jann Horn
2025-08-05 18:48   ` Zi Yan
2025-08-05 18:56     ` Lorenzo Stoakes
2025-08-05 19:00 ` Lorenzo Stoakes
2025-08-05 19:09   ` Zi Yan
2025-08-05 19:21     ` Lorenzo Stoakes
2025-08-06 10:34       ` Pedro Falcato
2025-08-06 10:37         ` Lorenzo Stoakes
2025-08-06  1:44     ` wang lian
2025-08-05 21:05 ` David Hildenbrand
2025-08-06  2:07 ` Wei Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).