BACKPORT: mm: Add fault_in_subpage_writeable() to probe at sub-page granularity
(Backport: no conflict, neighboring lines changes.) On hardware with features like arm64 MTE or SPARC ADI, an access fault can be triggered at sub-page granularity. Depending on how the fault_in_writeable() function is used, the caller can get into a live-lock by continuously retrying the fault-in on an address different from the one where the uaccess failed. In the majority of cases progress is ensured by the following conditions: 1. copy_to_user_nofault() guarantees at least one byte access if the user address is not faulting. 2. The fault_in_writeable() loop is resumed from the first address that could not be accessed by copy_to_user_nofault(). If the loop iteration is restarted from an earlier (initial) point, the loop is repeated with the same conditions and it would live-lock. Introduce an arch-specific probe_subpage_writeable() and call it from the newly added fault_in_subpage_writeable() function. The arch code with sub-page faults will have to implement the specific probing functionality. Note that no other fault_in_subpage_*() functions are added since they have no callers currently susceptible to a live-lock. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Andrew Morton <akpm@linux-foundation.org> Link: https://lore.kernel.org/r/20220423100751.1870771-2-catalin.marinas@arm.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Bug: 254721825 (cherry picked from commit da32b5817253697671af961715517bfbb308a592) Change-Id: I8362937496a2a8709686af9f97009b00a21b1f5d Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
This commit is contained in:
committed by
Todd Kjos
parent
4e64489f18
commit
e4b506cb0a
@@ -24,6 +24,13 @@ config KEXEC_ELF
|
|||||||
config HAVE_IMA_KEXEC
|
config HAVE_IMA_KEXEC
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config ARCH_HAS_SUBPAGE_FAULTS
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Select if the architecture can check permissions at sub-page
|
||||||
|
granularity (e.g. arm64 MTE). The probe_user_*() functions
|
||||||
|
must be implemented.
|
||||||
|
|
||||||
config SET_FS
|
config SET_FS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
|||||||
@@ -734,6 +734,7 @@ extern void add_page_wait_queue(struct page *page, wait_queue_entry_t *waiter);
|
|||||||
* Fault in userspace address range.
|
* Fault in userspace address range.
|
||||||
*/
|
*/
|
||||||
size_t fault_in_writeable(char __user *uaddr, size_t size);
|
size_t fault_in_writeable(char __user *uaddr, size_t size);
|
||||||
|
size_t fault_in_subpage_writeable(char __user *uaddr, size_t size);
|
||||||
size_t fault_in_safe_writeable(const char __user *uaddr, size_t size);
|
size_t fault_in_safe_writeable(const char __user *uaddr, size_t size);
|
||||||
size_t fault_in_readable(const char __user *uaddr, size_t size);
|
size_t fault_in_readable(const char __user *uaddr, size_t size);
|
||||||
|
|
||||||
|
|||||||
@@ -271,6 +271,28 @@ static inline bool pagefault_disabled(void)
|
|||||||
*/
|
*/
|
||||||
#define faulthandler_disabled() (pagefault_disabled() || in_atomic())
|
#define faulthandler_disabled() (pagefault_disabled() || in_atomic())
|
||||||
|
|
||||||
|
#ifndef CONFIG_ARCH_HAS_SUBPAGE_FAULTS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* probe_subpage_writeable: probe the user range for write faults at sub-page
|
||||||
|
* granularity (e.g. arm64 MTE)
|
||||||
|
* @uaddr: start of address range
|
||||||
|
* @size: size of address range
|
||||||
|
*
|
||||||
|
* Returns 0 on success, the number of bytes not probed on fault.
|
||||||
|
*
|
||||||
|
* It is expected that the caller checked for the write permission of each
|
||||||
|
* page in the range either by put_user() or GUP. The architecture port can
|
||||||
|
* implement a more efficient get_user() probing if the same sub-page faults
|
||||||
|
* are triggered by either a read or a write.
|
||||||
|
*/
|
||||||
|
static inline size_t probe_subpage_writeable(char __user *uaddr, size_t size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_ARCH_HAS_SUBPAGE_FAULTS */
|
||||||
|
|
||||||
#ifndef ARCH_HAS_NOCACHE_UACCESS
|
#ifndef ARCH_HAS_NOCACHE_UACCESS
|
||||||
|
|
||||||
static inline __must_check unsigned long
|
static inline __must_check unsigned long
|
||||||
|
|||||||
29
mm/gup.c
29
mm/gup.c
@@ -1730,6 +1730,35 @@ out:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(fault_in_writeable);
|
EXPORT_SYMBOL(fault_in_writeable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fault_in_subpage_writeable - fault in an address range for writing
|
||||||
|
* @uaddr: start of address range
|
||||||
|
* @size: size of address range
|
||||||
|
*
|
||||||
|
* Fault in a user address range for writing while checking for permissions at
|
||||||
|
* sub-page granularity (e.g. arm64 MTE). This function should be used when
|
||||||
|
* the caller cannot guarantee forward progress of a copy_to_user() loop.
|
||||||
|
*
|
||||||
|
* Returns the number of bytes not faulted in (like copy_to_user() and
|
||||||
|
* copy_from_user()).
|
||||||
|
*/
|
||||||
|
size_t fault_in_subpage_writeable(char __user *uaddr, size_t size)
|
||||||
|
{
|
||||||
|
size_t faulted_in;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt faulting in at page granularity first for page table
|
||||||
|
* permission checking. The arch-specific probe_subpage_writeable()
|
||||||
|
* functions may not check for this.
|
||||||
|
*/
|
||||||
|
faulted_in = size - fault_in_writeable(uaddr, size);
|
||||||
|
if (faulted_in)
|
||||||
|
faulted_in -= probe_subpage_writeable(uaddr, faulted_in);
|
||||||
|
|
||||||
|
return size - faulted_in;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(fault_in_subpage_writeable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fault_in_safe_writeable - fault in an address range for writing
|
* fault_in_safe_writeable - fault in an address range for writing
|
||||||
* @uaddr: start of address range
|
* @uaddr: start of address range
|
||||||
|
|||||||
Reference in New Issue
Block a user