summaryrefslogtreecommitdiff
path: root/mm
diff options
context:
space:
mode:
Diffstat (limited to 'mm')
-rw-r--r--mm/bootmem.c6
-rw-r--r--mm/cma.c2
-rw-r--r--mm/filemap.c2
-rw-r--r--mm/kmemleak.c47
-rw-r--r--mm/memblock.c8
-rw-r--r--mm/mprotect.c90
-rw-r--r--mm/nobootmem.c2
-rw-r--r--mm/shmem.c39
8 files changed, 154 insertions, 42 deletions
diff --git a/mm/bootmem.c b/mm/bootmem.c
index a869f84f44d3..e8a55a3c9feb 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -155,7 +155,7 @@ void __init free_bootmem_late(unsigned long physaddr, unsigned long size)
{
unsigned long cursor, end;
- kmemleak_free_part(__va(physaddr), size);
+ kmemleak_free_part_phys(physaddr, size);
cursor = PFN_UP(physaddr);
end = PFN_DOWN(physaddr + size);
@@ -399,7 +399,7 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
{
unsigned long start, end;
- kmemleak_free_part(__va(physaddr), size);
+ kmemleak_free_part_phys(physaddr, size);
start = PFN_UP(physaddr);
end = PFN_DOWN(physaddr + size);
@@ -420,7 +420,7 @@ void __init free_bootmem(unsigned long physaddr, unsigned long size)
{
unsigned long start, end;
- kmemleak_free_part(__va(physaddr), size);
+ kmemleak_free_part_phys(physaddr, size);
start = PFN_UP(physaddr);
end = PFN_DOWN(physaddr + size);
diff --git a/mm/cma.c b/mm/cma.c
index bd0e1412475e..384c2cb51b56 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -336,7 +336,7 @@ int __init cma_declare_contiguous(phys_addr_t base,
* kmemleak scans/reads tracked objects for pointers to other
* objects but this address isn't mapped and accessible
*/
- kmemleak_ignore(phys_to_virt(addr));
+ kmemleak_ignore_phys(addr);
base = addr;
}
diff --git a/mm/filemap.c b/mm/filemap.c
index 2f7b7783bd6b..849f459ad078 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1941,7 +1941,7 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
file_accessed(file);
retval = mapping->a_ops->direct_IO(iocb, &data);
- if (retval > 0) {
+ if (retval >= 0) {
iocb->ki_pos += retval;
iov_iter_advance(iter, retval);
}
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 086292f7c59d..a5e453cf05c4 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -90,6 +90,8 @@
#include <linux/cache.h>
#include <linux/percpu.h>
#include <linux/hardirq.h>
+#include <linux/bootmem.h>
+#include <linux/pfn.h>
#include <linux/mmzone.h>
#include <linux/slab.h>
#include <linux/thread_info.h>
@@ -1121,6 +1123,51 @@ void __ref kmemleak_no_scan(const void *ptr)
}
EXPORT_SYMBOL(kmemleak_no_scan);
+/**
+ * kmemleak_alloc_phys - similar to kmemleak_alloc but taking a physical
+ * address argument
+ */
+void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
+ gfp_t gfp)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_alloc(__va(phys), size, min_count, gfp);
+}
+EXPORT_SYMBOL(kmemleak_alloc_phys);
+
+/**
+ * kmemleak_free_part_phys - similar to kmemleak_free_part but taking a
+ * physical address argument
+ */
+void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_free_part(__va(phys), size);
+}
+EXPORT_SYMBOL(kmemleak_free_part_phys);
+
+/**
+ * kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical
+ * address argument
+ */
+void __ref kmemleak_not_leak_phys(phys_addr_t phys)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_not_leak(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_not_leak_phys);
+
+/**
+ * kmemleak_ignore_phys - similar to kmemleak_ignore but taking a physical
+ * address argument
+ */
+void __ref kmemleak_ignore_phys(phys_addr_t phys)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_ignore(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_ignore_phys);
+
/*
* Update an object's checksum and return true if it was modified.
*/
diff --git a/mm/memblock.c b/mm/memblock.c
index c8dfa430342b..7608bc305936 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -723,7 +723,7 @@ int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
(unsigned long long)base + size - 1,
(void *)_RET_IP_);
- kmemleak_free_part(__va(base), size);
+ kmemleak_free_part_phys(base, size);
return memblock_remove_range(&memblock.reserved, base, size);
}
@@ -1152,7 +1152,7 @@ static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,
* The min_count is set to 0 so that memblock allocations are
* never reported as leaks.
*/
- kmemleak_alloc(__va(found), size, 0, 0);
+ kmemleak_alloc_phys(found, size, 0, 0);
return found;
}
return 0;
@@ -1399,7 +1399,7 @@ void __init __memblock_free_early(phys_addr_t base, phys_addr_t size)
memblock_dbg("%s: [%#016llx-%#016llx] %pF\n",
__func__, (u64)base, (u64)base + size - 1,
(void *)_RET_IP_);
- kmemleak_free_part(__va(base), size);
+ kmemleak_free_part_phys(base, size);
memblock_remove_range(&memblock.reserved, base, size);
}
@@ -1419,7 +1419,7 @@ void __init __memblock_free_late(phys_addr_t base, phys_addr_t size)
memblock_dbg("%s: [%#016llx-%#016llx] %pF\n",
__func__, (u64)base, (u64)base + size - 1,
(void *)_RET_IP_);
- kmemleak_free_part(__va(base), size);
+ kmemleak_free_part_phys(base, size);
cursor = PFN_UP(base);
end = PFN_DOWN(base + size);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index ec91dfd3f900..bcdbe62f3e6d 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -23,11 +23,13 @@
#include <linux/mmu_notifier.h>
#include <linux/migrate.h>
#include <linux/perf_event.h>
+#include <linux/pkeys.h>
#include <linux/ksm.h>
#include <linux/pkeys.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#include "internal.h"
@@ -353,8 +355,11 @@ fail:
return error;
}
-SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
- unsigned long, prot)
+/*
+ * pkey==-1 when doing a legacy mprotect()
+ */
+static int do_mprotect_pkey(unsigned long start, size_t len,
+ unsigned long prot, int pkey)
{
unsigned long nstart, end, tmp, reqprot;
struct vm_area_struct *vma, *prev;
@@ -383,6 +388,14 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
if (down_write_killable(&current->mm->mmap_sem))
return -EINTR;
+ /*
+ * If userspace did not allocate the pkey, do not let
+ * them use it here.
+ */
+ error = -EINVAL;
+ if ((pkey != -1) && !mm_pkey_is_allocated(current->mm, pkey))
+ goto out;
+
vma = find_vma(current->mm, start);
error = -ENOMEM;
if (!vma)
@@ -409,8 +422,9 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
prev = vma;
for (nstart = start ; ; ) {
+ unsigned long mask_off_old_flags;
unsigned long newflags;
- int pkey = arch_override_mprotect_pkey(vma, prot, -1);
+ int new_vma_pkey;
/* Here we know that vma->vm_start <= nstart < vma->vm_end. */
@@ -418,8 +432,17 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
if (rier && (vma->vm_flags & VM_MAYEXEC))
prot |= PROT_EXEC;
- newflags = calc_vm_prot_bits(prot, pkey);
- newflags |= (vma->vm_flags & ~(VM_READ | VM_WRITE | VM_EXEC));
+ /*
+ * Each mprotect() call explicitly passes r/w/x permissions.
+ * If a permission is not passed to mprotect(), it must be
+ * cleared from the VMA.
+ */
+ mask_off_old_flags = VM_READ | VM_WRITE | VM_EXEC |
+ ARCH_VM_PKEY_FLAGS;
+
+ new_vma_pkey = arch_override_mprotect_pkey(vma, prot, pkey);
+ newflags = calc_vm_prot_bits(prot, new_vma_pkey);
+ newflags |= (vma->vm_flags & ~mask_off_old_flags);
/* newflags >> 4 shift VM_MAY% in place of VM_% */
if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) {
@@ -455,3 +478,60 @@ out:
up_write(&current->mm->mmap_sem);
return error;
}
+
+SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len,
+ unsigned long, prot)
+{
+ return do_mprotect_pkey(start, len, prot, -1);
+}
+
+SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
+ unsigned long, prot, int, pkey)
+{
+ return do_mprotect_pkey(start, len, prot, pkey);
+}
+
+SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val)
+{
+ int pkey;
+ int ret;
+
+ /* No flags supported yet. */
+ if (flags)
+ return -EINVAL;
+ /* check for unsupported init values */
+ if (init_val & ~PKEY_ACCESS_MASK)
+ return -EINVAL;
+
+ down_write(&current->mm->mmap_sem);
+ pkey = mm_pkey_alloc(current->mm);
+
+ ret = -ENOSPC;
+ if (pkey == -1)
+ goto out;
+
+ ret = arch_set_user_pkey_access(current, pkey, init_val);
+ if (ret) {
+ mm_pkey_free(current->mm, pkey);
+ goto out;
+ }
+ ret = pkey;
+out:
+ up_write(&current->mm->mmap_sem);
+ return ret;
+}
+
+SYSCALL_DEFINE1(pkey_free, int, pkey)
+{
+ int ret;
+
+ down_write(&current->mm->mmap_sem);
+ ret = mm_pkey_free(current->mm, pkey);
+ up_write(&current->mm->mmap_sem);
+
+ /*
+ * We could provie warnings or errors if any VMA still
+ * has the pkey set here.
+ */
+ return ret;
+}
diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index ba609b684d7a..487dad610731 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -84,7 +84,7 @@ void __init free_bootmem_late(unsigned long addr, unsigned long size)
{
unsigned long cursor, end;
- kmemleak_free_part(__va(addr), size);
+ kmemleak_free_part_phys(addr, size);
cursor = PFN_UP(addr);
end = PFN_DOWN(addr + size);
diff --git a/mm/shmem.c b/mm/shmem.c
index 0e9901e69d24..ad7813d73ea7 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -934,7 +934,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
{
shmem_undo_range(inode, lstart, lend, false);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_ctime = inode->i_mtime = current_time(inode);
}
EXPORT_SYMBOL_GPL(shmem_truncate_range);
@@ -960,7 +960,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
int error;
- error = inode_change_ok(inode, attr);
+ error = setattr_prepare(dentry, attr);
if (error)
return error;
@@ -979,7 +979,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
if (error)
return error;
i_size_write(inode, newsize);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_ctime = inode->i_mtime = current_time(inode);
}
if (newsize <= oldsize) {
loff_t holebegin = round_up(newsize, PAGE_SIZE);
@@ -2083,7 +2083,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
inode->i_ino = get_next_ino();
inode_init_owner(inode, dir, mode);
inode->i_blocks = 0;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_generation = get_seconds();
info = SHMEM_I(inode);
memset(info, 0, (char *)inode - (char *)info);
@@ -2741,7 +2741,7 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + len > inode->i_size)
i_size_write(inode, offset + len);
- inode->i_ctime = CURRENT_TIME;
+ inode->i_ctime = current_time(inode);
undone:
spin_lock(&inode->i_lock);
inode->i_private = NULL;
@@ -2794,7 +2794,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
error = 0;
dir->i_size += BOGO_DIRENT_SIZE;
- dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_ctime = dir->i_mtime = current_time(dir);
d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
}
@@ -2862,7 +2862,7 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentr
goto out;
dir->i_size += BOGO_DIRENT_SIZE;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
inc_nlink(inode);
ihold(inode); /* New dentry reference */
dget(dentry); /* Extra pinning count for the created dentry */
@@ -2879,7 +2879,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
shmem_free_inode(inode->i_sb);
dir->i_size -= BOGO_DIRENT_SIZE;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);
drop_nlink(inode);
dput(dentry); /* Undo the count from "create" - this does all the work */
return 0;
@@ -2912,7 +2912,7 @@ static int shmem_exchange(struct inode *old_dir, struct dentry *old_dentry, stru
old_dir->i_ctime = old_dir->i_mtime =
new_dir->i_ctime = new_dir->i_mtime =
d_inode(old_dentry)->i_ctime =
- d_inode(new_dentry)->i_ctime = CURRENT_TIME;
+ d_inode(new_dentry)->i_ctime = current_time(old_dir);
return 0;
}
@@ -2986,7 +2986,7 @@ static int shmem_rename2(struct inode *old_dir, struct dentry *old_dentry, struc
new_dir->i_size += BOGO_DIRENT_SIZE;
old_dir->i_ctime = old_dir->i_mtime =
new_dir->i_ctime = new_dir->i_mtime =
- inode->i_ctime = CURRENT_TIME;
+ inode->i_ctime = current_time(old_dir);
return 0;
}
@@ -3041,7 +3041,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
put_page(page);
}
dir->i_size += BOGO_DIRENT_SIZE;
- dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_ctime = dir->i_mtime = current_time(dir);
d_instantiate(dentry, inode);
dget(dentry);
return 0;
@@ -3175,10 +3175,7 @@ static const struct inode_operations shmem_short_symlink_operations = {
.readlink = generic_readlink,
.get_link = simple_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -3186,10 +3183,7 @@ static const struct inode_operations shmem_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = shmem_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
};
@@ -3683,10 +3677,7 @@ static const struct inode_operations shmem_inode_operations = {
.getattr = shmem_getattr,
.setattr = shmem_setattr,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
.set_acl = simple_set_acl,
#endif
};
@@ -3701,14 +3692,11 @@ static const struct inode_operations shmem_dir_inode_operations = {
.mkdir = shmem_mkdir,
.rmdir = shmem_rmdir,
.mknod = shmem_mknod,
- .rename2 = shmem_rename2,
+ .rename = shmem_rename2,
.tmpfile = shmem_tmpfile,
#endif
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,
@@ -3718,10 +3706,7 @@ static const struct inode_operations shmem_dir_inode_operations = {
static const struct inode_operations shmem_special_inode_operations = {
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = generic_setxattr,
- .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,