Commit a06db751 authored by Konstantin Khlebnikov's avatar Konstantin Khlebnikov Committed by Linus Torvalds

pagemap: check permissions and capabilities at open time

This patchset makes pagemap useable again in the safe way (after row
hammer bug it was made CAP_SYS_ADMIN-only).  This patchset restores access
for non-privileged users but hides PFNs from them.

Also it adds bit 'map-exclusive' which is set if page is mapped only here:
it helps in estimation of working set without exposing pfns and allows to
distinguish CoWed and non-CoWed private anonymous pages.

Second patch removes page-shift bits and completes migration to the new
pagemap format: flags soft-dirty and mmap-exclusive are available only in
the new format.

This patch (of 5):

This patch moves permission checks from pagemap_read() into pagemap_open().

Pointer to mm is saved in file->private_data. This reference pins only
mm_struct itself. /proc/*/mem, maps, smaps already work in the same way.

See http://lkml.kernel.org/r/CA+55aFyKpWrt_Ajzh1rzp_GcwZ4=6Y=kOv8hBz172CFJp6L8Tg@mail.gmail.comSigned-off-by: default avatarKonstantin Khlebnikov <khlebnikov@yandex-team.ru>
Reviewed-by: default avatarNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Reviewed-by: default avatarMark Williamson <mwilliamson@undo-software.com>
Tested-by: default avatarMark Williamson <mwilliamson@undo-software.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b5e3aa0a
...@@ -1229,40 +1229,33 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask, ...@@ -1229,40 +1229,33 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask,
static ssize_t pagemap_read(struct file *file, char __user *buf, static ssize_t pagemap_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct task_struct *task = get_proc_task(file_inode(file)); struct mm_struct *mm = file->private_data;
struct mm_struct *mm;
struct pagemapread pm; struct pagemapread pm;
int ret = -ESRCH;
struct mm_walk pagemap_walk = {}; struct mm_walk pagemap_walk = {};
unsigned long src; unsigned long src;
unsigned long svpfn; unsigned long svpfn;
unsigned long start_vaddr; unsigned long start_vaddr;
unsigned long end_vaddr; unsigned long end_vaddr;
int copied = 0; int ret = 0, copied = 0;
if (!task) if (!mm || !atomic_inc_not_zero(&mm->mm_users))
goto out; goto out;
ret = -EINVAL; ret = -EINVAL;
/* file position must be aligned */ /* file position must be aligned */
if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES)) if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES))
goto out_task; goto out_mm;
ret = 0; ret = 0;
if (!count) if (!count)
goto out_task; goto out_mm;
pm.v2 = soft_dirty_cleared; pm.v2 = soft_dirty_cleared;
pm.len = (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); pm.len = (PAGEMAP_WALK_SIZE >> PAGE_SHIFT);
pm.buffer = kmalloc(pm.len * PM_ENTRY_BYTES, GFP_TEMPORARY); pm.buffer = kmalloc(pm.len * PM_ENTRY_BYTES, GFP_TEMPORARY);
ret = -ENOMEM; ret = -ENOMEM;
if (!pm.buffer) if (!pm.buffer)
goto out_task; goto out_mm;
mm = mm_access(task, PTRACE_MODE_READ);
ret = PTR_ERR(mm);
if (!mm || IS_ERR(mm))
goto out_free;
pagemap_walk.pmd_entry = pagemap_pte_range; pagemap_walk.pmd_entry = pagemap_pte_range;
pagemap_walk.pte_hole = pagemap_pte_hole; pagemap_walk.pte_hole = pagemap_pte_hole;
...@@ -1275,10 +1268,10 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -1275,10 +1268,10 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
src = *ppos; src = *ppos;
svpfn = src / PM_ENTRY_BYTES; svpfn = src / PM_ENTRY_BYTES;
start_vaddr = svpfn << PAGE_SHIFT; start_vaddr = svpfn << PAGE_SHIFT;
end_vaddr = TASK_SIZE_OF(task); end_vaddr = mm->task_size;
/* watch out for wraparound */ /* watch out for wraparound */
if (svpfn > TASK_SIZE_OF(task) >> PAGE_SHIFT) if (svpfn > mm->task_size >> PAGE_SHIFT)
start_vaddr = end_vaddr; start_vaddr = end_vaddr;
/* /*
...@@ -1305,7 +1298,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -1305,7 +1298,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
len = min(count, PM_ENTRY_BYTES * pm.pos); len = min(count, PM_ENTRY_BYTES * pm.pos);
if (copy_to_user(buf, pm.buffer, len)) { if (copy_to_user(buf, pm.buffer, len)) {
ret = -EFAULT; ret = -EFAULT;
goto out_mm; goto out_free;
} }
copied += len; copied += len;
buf += len; buf += len;
...@@ -1315,24 +1308,38 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, ...@@ -1315,24 +1308,38 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
if (!ret || ret == PM_END_OF_BUFFER) if (!ret || ret == PM_END_OF_BUFFER)
ret = copied; ret = copied;
out_mm:
mmput(mm);
out_free: out_free:
kfree(pm.buffer); kfree(pm.buffer);
out_task: out_mm:
put_task_struct(task); mmput(mm);
out: out:
return ret; return ret;
} }
static int pagemap_open(struct inode *inode, struct file *file) static int pagemap_open(struct inode *inode, struct file *file)
{ {
struct mm_struct *mm;
/* do not disclose physical addresses: attack vector */ /* do not disclose physical addresses: attack vector */
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
pr_warn_once("Bits 55-60 of /proc/PID/pagemap entries are about " pr_warn_once("Bits 55-60 of /proc/PID/pagemap entries are about "
"to stop being page-shift some time soon. See the " "to stop being page-shift some time soon. See the "
"linux/Documentation/vm/pagemap.txt for details.\n"); "linux/Documentation/vm/pagemap.txt for details.\n");
mm = proc_mem_open(inode, PTRACE_MODE_READ);
if (IS_ERR(mm))
return PTR_ERR(mm);
file->private_data = mm;
return 0;
}
static int pagemap_release(struct inode *inode, struct file *file)
{
struct mm_struct *mm = file->private_data;
if (mm)
mmdrop(mm);
return 0; return 0;
} }
...@@ -1340,6 +1347,7 @@ const struct file_operations proc_pagemap_operations = { ...@@ -1340,6 +1347,7 @@ const struct file_operations proc_pagemap_operations = {
.llseek = mem_lseek, /* borrow this */ .llseek = mem_lseek, /* borrow this */
.read = pagemap_read, .read = pagemap_read,
.open = pagemap_open, .open = pagemap_open,
.release = pagemap_release,
}; };
#endif /* CONFIG_PROC_PAGE_MONITOR */ #endif /* CONFIG_PROC_PAGE_MONITOR */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment