Commit 8c509665 authored by ZhangPeng's avatar ZhangPeng Committed by Ma Wupeng
Browse files

userswap: split uswap_register() to validate address ranges

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I6CAIM



--------------------------------

Split uswap_register() into uswap_register() and uswap_adjust_uffd_range().
Before validate_range(), use uswap_register() to handle uswap mode.
After validate_range(), use uswap_adjust_uffd_range() to change address
range to VMA range, which could reduce fragmentation caused by VMA
splitting.
By splitting uswap_register(), we could prevent the userswap registration
of invalid input address ranges.

Signed-off-by: default avatarZhangPeng <zhangpeng362@huawei.com>
parent 2e04865a
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -1282,6 +1282,9 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
	bool found;
	bool basic_ioctls;
	unsigned long start, end, vma_end;
#ifdef CONFIG_USERSWAP
	bool uswap_mode = false;
#endif

	user_uffdio_register = (struct uffdio_register __user *) arg;

@@ -1295,7 +1298,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
		goto out;
	vm_flags = 0;
#ifdef CONFIG_USERSWAP
	if (!uswap_register(&uffdio_register, &vm_flags, mm))
	if (!uswap_register(&uffdio_register, &uswap_mode))
		goto out;
#endif
	if (uffdio_register.mode & ~(UFFDIO_REGISTER_MODE_MISSING|
@@ -1310,7 +1313,14 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
			     uffdio_register.range.len);
	if (ret)
		goto out;

#ifdef CONFIG_USERSWAP
	if (uswap_mode)
		if (!uswap_adjust_uffd_range(&uffdio_register, &vm_flags,
					     mm)) {
			ret = -EINVAL;
			goto out;
		}
#endif
	start = uffdio_register.range.start;
	end = start + uffdio_register.range.len;

+4 −2
Original line number Diff line number Diff line
@@ -28,7 +28,9 @@ int mfill_atomic_pte_nocopy(struct mm_struct *dst_mm,
unsigned long uswap_mremap(unsigned long old_addr, unsigned long old_len,
			   unsigned long new_addr, unsigned long new_len);

bool uswap_register(struct uffdio_register *uffdio_register,
bool uswap_register(struct uffdio_register *uffdio_register, bool *uswap_mode);

bool uswap_adjust_uffd_range(struct uffdio_register *uffdio_register,
			     unsigned long *vm_flags, struct mm_struct *mm);

bool do_uswap_page(swp_entry_t entry, struct vm_fault *vmf,
+17 −9
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/mmu_notifier.h>
#include <linux/userswap.h>
#include <linux/userfaultfd_k.h>
#include <linux/security.h>

#include "internal.h"

@@ -462,11 +463,24 @@ int mfill_atomic_pte_nocopy(struct mm_struct *mm,
	return ret;
}

bool uswap_register(struct uffdio_register *uffdio_register, bool *uswap_mode)
{
	if (!static_branch_unlikely(&userswap_enabled))
		return true;
	if (!(uffdio_register->mode & UFFDIO_REGISTER_MODE_USWAP))
		return true;
	uffdio_register->mode &= ~UFFDIO_REGISTER_MODE_USWAP;
	if (!uffdio_register->mode)
		return false;
	*uswap_mode = true;
	return true;
}

/*
 * register the whole vma overlapping with the address range to avoid splitting
 * the vma.
 * the vma which could reduce fragmentation.
 */
bool uswap_register(struct uffdio_register *uffdio_register,
bool uswap_adjust_uffd_range(struct uffdio_register *uffdio_register,
			     unsigned long *vm_flags, struct mm_struct *mm)
{
	struct vm_area_struct *vma;
@@ -474,12 +488,6 @@ bool uswap_register(struct uffdio_register *uffdio_register,

	if (!static_branch_unlikely(&userswap_enabled))
		return true;
	if (!(uffdio_register->mode & UFFDIO_REGISTER_MODE_USWAP))
		return true;
	uffdio_register->mode &= ~UFFDIO_REGISTER_MODE_USWAP;
	if (!uffdio_register->mode)
		return false;

	end = uffdio_register->range.start + uffdio_register->range.len - 1;
	vma = find_vma(mm, uffdio_register->range.start);
	if (!vma)