Skip to contentSkip to conversation

mm/userfaultfd: provide unmasked address on page-fault

PATCH Superseded Nadav Amit 3 participants 5 messages Oct 8, 2021 – Oct 10, 2021

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Nadav Amit

author
#0 lore raw

PATCH 1/1 mm/userfaultfd: provide unmasked address on page-fault

From: Nadav Amit <[email protected]>

Userfaultfd is supposed to provide the full address (i.e., unmasked) of the faulting access back to userspace. However, that is not the case for quite some time.

Even running "userfaultfd_demo" from the userfaultfd man page provides the wrong output (and contradicts the man page). Notice that "UFFD_EVENT_PAGEFAULT event" shows the masked address.

	Address returned by mmap() = 0x7fc5e30b3000

	fault_handler_thread():
	    poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
	    UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fc5e30b3000
		(uffdio_copy.copy returned 4096)
	Read address 0x7fc5e30b300f in main(): A
	Read address 0x7fc5e30b340f in main(): A
	Read address 0x7fc5e30b380f in main(): A
	Read address 0x7fc5e30b3c0f in main(): A

Add a new "real_address" field to vmf to hold the unmasked address. It is possible to keep the unmasked address in the existing address field (and mask whenever necessary) instead, but this is likely to cause backporting problems of this patch.

Cc Andrea Arcangeli <[email protected]>
Cc Mike Rapoport <[email protected]>
Cc Peter Xu <[email protected]>
Cc Jan Kara <[email protected]>
Fixes 1a29d85eb0f19 ("mm: use vmf->address instead of of vmf->virtual_address")
---
 fs/userfaultfd.c   | 2 +-
 include/linux/mm.h | 3 ++-
 mm/memory.c        | 1 +
 3 files changed, 4 insertions(+), 2 deletions(-)
fs/userfaultfd.c +1 -1
index 003f0d31743e..1dfc0fcd83c1 100644
@@ -481,7 +481,7 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason)
 
 	init_waitqueue_func_entry(&uwq.wq, userfaultfd_wake_function);
 	uwq.wq.private = current;
-	uwq.msg = userfault_msg(vmf->address, vmf->flags, reason,
+	uwq.msg = userfault_msg(vmf->real_address, vmf->flags, reason,
 			ctx->features);
 	uwq.ctx = ctx;
 	uwq.waken = false;
include/linux/mm.h +2 -1
index 00bb2d938df4..f3f324e3f2bf 100644
@@ -523,7 +523,8 @@ struct vm_fault {
 		struct vm_area_struct *vma;	/* Target VMA */
 		gfp_t gfp_mask;			/* gfp mask to be used for allocations */
 		pgoff_t pgoff;			/* Logical page offset based on vma */
-		unsigned long address;		/* Faulting virtual address */
+		unsigned long address;		/* Faulting virtual address - masked */
+		unsigned long real_address;	/* Faulting virtual address - unmaked */
 	};
 	enum fault_flag flags;		/* FAULT_FLAG_xxx flags
 					 * XXX: should really be 'const' */
mm/memory.c +1 -0
index 12a7b2094434..3d2d7fdbb7dc 100644
@@ -4594,6 +4594,7 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
 	struct vm_fault vmf = {
 		.vma = vma,
 		.address = address & PAGE_MASK,
+		.real_address = address,
 		.flags = flags,
 		.pgoff = linear_page_index(vma, address),
 		.gfp_mask = __get_fault_gfp_mask(vma),
Signature · 1 line
2.25.1
#1 lore raw
Quoted 21 lines · Nadav Amit

From: Nadav Amit <[email protected]>

Userfaultfd is supposed to provide the full address (i.e., unmasked) of the faulting access back to userspace. However, that is not the case for quite some time.

Even running "userfaultfd_demo" from the userfaultfd man page provides the wrong output (and contradicts the man page). Notice that "UFFD_EVENT_PAGEFAULT event" shows the masked address.

	Address returned by mmap() = 0x7fc5e30b3000

	fault_handler_thread():
	    poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
	    UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fc5e30b3000
		(uffdio_copy.copy returned 4096)
	Read address 0x7fc5e30b300f in main(): A
	Read address 0x7fc5e30b340f in main(): A
	Read address 0x7fc5e30b380f in main(): A
	Read address 0x7fc5e30b3c0f in main(): A
Quoted 4 lines

Add a new "real_address" field to vmf to hold the unmasked address. It is possible to keep the unmasked address in the existing address field (and mask whenever necessary) instead, but this is likely to cause backporting problems of this patch.

Can we be sure that no existing users will rely on this behavior that has been the case since end of 2016 IIRC, one year after UFFD was upstreamed? I do wonder what the official ABI nowadays is, because man pages aren't necessarily the source of truth.

I checked QEMU (postcopy live migration), and I think it should be fine with this change.

If we don't want to change the current ABI behavior, we could add a new feature flag to change behavior.

@Peter, what are your thoughts?

Signature · 3 lines
Thanks,

David / dhildenb
author
#2 lore raw
Quoted 26 lines · David Hildenbrand
Quoted 16 lines · Nadav Amit

From: Nadav Amit <[email protected]>
Userfaultfd is supposed to provide the full address (i.e., unmasked) of the faulting access back to userspace. However, that is not the case for quite some time.
Even running "userfaultfd_demo" from the userfaultfd man page provides the wrong output (and contradicts the man page). Notice that "UFFD_EVENT_PAGEFAULT event" shows the masked address.

	Address returned by mmap() = 0x7fc5e30b3000
	fault_handler_thread():
	    poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
	    UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fc5e30b3000
		(uffdio_copy.copy returned 4096)
	Read address 0x7fc5e30b300f in main(): A
	Read address 0x7fc5e30b340f in main(): A
	Read address 0x7fc5e30b380f in main(): A
	Read address 0x7fc5e30b3c0f in main(): A
Quoted 4 lines

Add a new "real_address" field to vmf to hold the unmasked address. It is possible to keep the unmasked address in the existing address field (and mask whenever necessary) instead, but this is likely to cause backporting problems of this patch.

Quoted 1 line

Can we be sure that no existing users will rely on this behavior that has been the case since end of 2016 IIRC, one year after UFFD was upstreamed?

Let me to blow off your mind: how do you be sure that the current behavior does not make applications to misbehave? It might cause performance issues as it did for me or hidden correctness issues.

Quoted 1 line

I do wonder what the official ABI nowadays is, because man pages aren't necessarily the source of truth.

Documentation/admin-guide/mm/userfaultfd.rst says: "You get the address of the access that triggered the missing page event”.

So it is a bug.

#3 lore raw
Quoted 26 lines · Nadav Amit
Quoted 26 lines · David Hildenbrand
Quoted 20 lines · Nadav Amit

From: Nadav Amit <[email protected]>
Userfaultfd is supposed to provide the full address (i.e., unmasked) of the faulting access back to userspace. However, that is not the case for quite some time.
Even running "userfaultfd_demo" from the userfaultfd man page provides the wrong output (and contradicts the man page). Notice that "UFFD_EVENT_PAGEFAULT event" shows the masked address.

	Address returned by mmap() = 0x7fc5e30b3000
	fault_handler_thread():
	    poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
	    UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fc5e30b3000
		(uffdio_copy.copy returned 4096)
	Read address 0x7fc5e30b300f in main(): A
	Read address 0x7fc5e30b340f in main(): A
	Read address 0x7fc5e30b380f in main(): A
	Read address 0x7fc5e30b3c0f in main(): A

Add a new "real_address" field to vmf to hold the unmasked address. It is possible to keep the unmasked address in the existing address field (and mask whenever necessary) instead, but this is likely to cause backporting problems of this patch.

Quoted 4 lines
Quoted 1 line

Can we be sure that no existing users will rely on this behavior that has been the case since end of 2016 IIRC, one year after UFFD was upstreamed?

Let me to blow off your mind: how do you be sure that the current behavior does not make applications to misbehave? It might cause performance issues as it did for me or hidden correctness issues.

Fair point, but now we can speculate what's more likely:

Having an app rely on >4 year old kernel behavior just after the feature was released or having and app rely on kernel behavior that was the case for the last 4 years?

<offtopic>
Someone once told me about the unwritten way to remove things from the kernel. 1) Silently break it upstream 2) Wait 2 kernel releases 3) Propose removal of the feature because it's broken and nobody complained. <\offtopic>

You might ask "why does David even care?", here is why:

For the records, I *do* have a prototype from last year that breaks with this new behavior as far as I can tell: using uffd in the context of virtio-balloon in QEMU. I just pushed the latest state to a !private github tree:
https://github.com/davidhildenbrand/qemu/tree/virtio-balloon-uffd

In that code, I made sure that I'm only dealing with 4k pages (because that's the only thing virtio-balloon really can deal with), and during the debugging I figured that the kernel always returns 4k aligned page fault addresses, so I didn't care about masking. I'll reuse the unmodified fault address for UFFDIO_ZEROPAGE()/UFFDIO_COPY()/... which should then fail because:

"
EINVAL The start or the len field of the ufdio_range structure
was not a multiple of the system page size; or len was zero; or the specified range was otherwise invalid.
"

If I'm too lazy to read all documentation, I'm quite sure that there are other people that don't. I don't care to much if this patch breaks that prototype, it's just a prototype after all, but I am concerned that we might break other users in a similar way.

Quoted 6 lines
Quoted 1 line

I do wonder what the official ABI nowadays is, because man pages aren't necessarily the source of truth.

Documentation/admin-guide/mm/userfaultfd.rst says: "You get the address of the access that triggered the missing page event”.

So it is a bug.

The least thing I would expect in the patch description is a better motivation ("who cares and why" -- I know you have a better motivation that making the doc correct :) ) and a discussion on the chances of this actually breaking other apps (see my example).

I'd sleep better if we'd glue the changed behavior to a new feature flag, but that's just my 2 cents.

Signature · 3 lines
Thanks,

David / dhildenb
#4 lore raw
In reply to David Hildenbrand
Quoted 27 lines · David Hildenbrand
Quoted 21 lines · Nadav Amit

From: Nadav Amit <[email protected]>

Userfaultfd is supposed to provide the full address (i.e., unmasked) of the faulting access back to userspace. However, that is not the case for quite some time.

Even running "userfaultfd_demo" from the userfaultfd man page provides the wrong output (and contradicts the man page). Notice that "UFFD_EVENT_PAGEFAULT event" shows the masked address.

	Address returned by mmap() = 0x7fc5e30b3000

	fault_handler_thread():
	    poll() returns: nready = 1; POLLIN = 1; POLLERR = 0
	    UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fc5e30b3000
		(uffdio_copy.copy returned 4096)
	Read address 0x7fc5e30b300f in main(): A
	Read address 0x7fc5e30b340f in main(): A
	Read address 0x7fc5e30b380f in main(): A
	Read address 0x7fc5e30b3c0f in main(): A
Quoted 4 lines

Add a new "real_address" field to vmf to hold the unmasked address. It is possible to keep the unmasked address in the existing address field (and mask whenever necessary) instead, but this is likely to cause backporting problems of this patch.

Quoted 7 lines

Can we be sure that no existing users will rely on this behavior that has been the case since end of 2016 IIRC, one year after UFFD was upstreamed? I do wonder what the official ABI nowadays is, because man pages aren't necessarily the source of truth.

I checked QEMU (postcopy live migration), and I think it should be fine with this change.

CRIU is Ok with this change, we anyway mask the address.

Signature · 2 lines
Sincerely yours,
Mike.