690c4ca8a5
(Upstream commit 057d3389108eda8a20c7f496f011846932680d88). This patch is a part of a series that extends kernel ABI to allow to pass tagged user pointers (with the top byte set to something else other than 0x00) as syscall arguments. This patch allows tagged pointers to be passed to the following memory syscalls: get_mempolicy, madvise, mbind, mincore, mlock, mlock2, mprotect, mremap, msync, munlock, move_pages. The mmap and mremap syscalls do not currently accept tagged addresses. Architectures may interpret the tag as a background colour for the corresponding vma. Link: http://lkml.kernel.org/r/aaf0c0969d46b2feb9017f3e1b3ef3970b633d91.1563904656.git.andreyknvl@google.com Signed-off-by: Andrey Konovalov <andreyknvl@google.com> Reviewed-by: Khalid Aziz <khalid.aziz@oracle.com> Reviewed-by: Vincenzo Frascino <vincenzo.frascino@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Kees Cook <keescook@chromium.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Eric Auger <eric.auger@redhat.com> Cc: Felix Kuehling <Felix.Kuehling@amd.com> Cc: Jens Wiklander <jens.wiklander@linaro.org> Cc: Mauro Carvalho Chehab <mchehab+samsung@kernel.org> Cc: Mike Rapoport <rppt@linux.ibm.com> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Andrey Konovalov <andreyknvl@google.com> Bug: 135692346 Change-Id: I1a2d89eedb45e618e85ca515f4c9121460711efb
110 lines
2.7 KiB
C
110 lines
2.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/mm/msync.c
|
|
*
|
|
* Copyright (C) 1994-1999 Linus Torvalds
|
|
*/
|
|
|
|
/*
|
|
* The msync() system call.
|
|
*/
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/file.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/sched.h>
|
|
|
|
/*
|
|
* MS_SYNC syncs the entire file - including mappings.
|
|
*
|
|
* MS_ASYNC does not start I/O (it used to, up to 2.5.67).
|
|
* Nor does it marks the relevant pages dirty (it used to up to 2.6.17).
|
|
* Now it doesn't do anything, since dirty pages are properly tracked.
|
|
*
|
|
* The application may now run fsync() to
|
|
* write out the dirty pages and wait on the writeout and check the result.
|
|
* Or the application may run fadvise(FADV_DONTNEED) against the fd to start
|
|
* async writeout immediately.
|
|
* So by _not_ starting I/O in MS_ASYNC we provide complete flexibility to
|
|
* applications.
|
|
*/
|
|
SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
|
|
{
|
|
unsigned long end;
|
|
struct mm_struct *mm = current->mm;
|
|
struct vm_area_struct *vma;
|
|
int unmapped_error = 0;
|
|
int error = -EINVAL;
|
|
|
|
start = untagged_addr(start);
|
|
|
|
if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))
|
|
goto out;
|
|
if (offset_in_page(start))
|
|
goto out;
|
|
if ((flags & MS_ASYNC) && (flags & MS_SYNC))
|
|
goto out;
|
|
error = -ENOMEM;
|
|
len = (len + ~PAGE_MASK) & PAGE_MASK;
|
|
end = start + len;
|
|
if (end < start)
|
|
goto out;
|
|
error = 0;
|
|
if (end == start)
|
|
goto out;
|
|
/*
|
|
* If the interval [start,end) covers some unmapped address ranges,
|
|
* just ignore them, but return -ENOMEM at the end.
|
|
*/
|
|
down_read(&mm->mmap_sem);
|
|
vma = find_vma(mm, start);
|
|
for (;;) {
|
|
struct file *file;
|
|
loff_t fstart, fend;
|
|
|
|
/* Still start < end. */
|
|
error = -ENOMEM;
|
|
if (!vma)
|
|
goto out_unlock;
|
|
/* Here start < vma->vm_end. */
|
|
if (start < vma->vm_start) {
|
|
start = vma->vm_start;
|
|
if (start >= end)
|
|
goto out_unlock;
|
|
unmapped_error = -ENOMEM;
|
|
}
|
|
/* Here vma->vm_start <= start < vma->vm_end. */
|
|
if ((flags & MS_INVALIDATE) &&
|
|
(vma->vm_flags & VM_LOCKED)) {
|
|
error = -EBUSY;
|
|
goto out_unlock;
|
|
}
|
|
file = vma->vm_file;
|
|
fstart = (start - vma->vm_start) +
|
|
((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
|
fend = fstart + (min(end, vma->vm_end) - start) - 1;
|
|
start = vma->vm_end;
|
|
if ((flags & MS_SYNC) && file &&
|
|
(vma->vm_flags & VM_SHARED)) {
|
|
get_file(file);
|
|
up_read(&mm->mmap_sem);
|
|
error = vfs_fsync_range(file, fstart, fend, 1);
|
|
fput(file);
|
|
if (error || start >= end)
|
|
goto out;
|
|
down_read(&mm->mmap_sem);
|
|
vma = find_vma(mm, start);
|
|
} else {
|
|
if (start >= end) {
|
|
error = 0;
|
|
goto out_unlock;
|
|
}
|
|
vma = vma->vm_next;
|
|
}
|
|
}
|
|
out_unlock:
|
|
up_read(&mm->mmap_sem);
|
|
out:
|
|
return error ? : unmapped_error;
|
|
}
|