#include <linux/rmap.h>
#include <linux/swap.h>
#include <linux/swapops.h>
+#include <linux/security.h>
#include <asm/elf.h>
#include <asm/uaccess.h>
return ret;
}
+#ifdef __arm__
+static void show_arm_map_vma(struct seq_file *m, struct vm_area_struct *vma)
+{
+ static const char *cache_attrs4[4] = { "noC", "WB-WA", "WT-noWA", "WB-noWA" };
+ struct mm_struct *mm = vma->vm_mm;
+ struct file *file = vma->vm_file;
+ vm_flags_t flags = vma->vm_flags;
+ unsigned long start, end, end_b;
+ const char *name = NULL;
+ u32 *arm_pgd;
+ u32 *cpt, *cpt_e;
+ u32 desc1, desc2;
+ u32 tex_cb = 0;
+ u32 prrr, nmrr = 0;
+ u32 control = 0;
+ u32 xn = 1, ap = 0;
+ int desc_type;
+ int type;
+ char buf[64];
+ char rw[4];
+ int len;
+ int s;
+
+ if (mm == NULL)
+ return;
+
+ if (!file) {
+ name = arch_vma_name(vma);
+ if (!name) {
+ if (vma->vm_start <= mm->brk &&
+ vma->vm_end >= mm->start_brk) {
+ name = "[heap]";
+ } else if (vma->vm_start <= mm->start_stack &&
+ vma->vm_end >= mm->start_stack) {
+ name = "[stack]";
+ }
+ }
+ }
+
+ arm_pgd = (u32 *)mm->pgd;
+
+ asm ("mrc p15, 0, %0, c1, c0, 0" : "=r"(control));
+ asm ("mrc p15, 0, %0, c10, c2, 0" : "=r"(prrr)); // primary region RR
+ asm ("mrc p15, 0, %0, c10, c2, 1" : "=r"(nmrr)); // normal memory RR
+
+ start = vma->vm_start;
+ end = vma->vm_end;
+
+ while (start < end) {
+ desc_type = '-';
+
+ desc1 = arm_pgd[start >> 20];
+
+ end_b = (start & ~0xfffff) + 0x100000;
+ for (; end_b < end; end_b += 0x100000)
+ if ((arm_pgd[end_b >> 20] ^ desc1) & 0xfffff)
+ break;
+
+ switch (desc1 & 3) {
+ case 0:
+ sprintf(buf, "l1_fault");
+ goto do_output;
+ case 1:
+ break;
+ case 2:
+ tex_cb = ((desc1 >> 2) & 0x03) | ((desc1 >> 10) & 0x1c);
+ s = (desc1 >> 16) & 1;
+ xn = (desc1 >> 4) & 1;
+ ap = ((desc1 >> 10) & 3) | ((desc1 >> 13) & 4);
+ desc_type = (desc1 & (1 << 18)) ? 's' : 'h';
+ goto do_tex_cb;
+ case 3:
+ sprintf(buf, "reserved");
+ goto do_output;
+ }
+
+ cpt = __va(desc1 & 0xfffffc00);
+ desc2 = cpt[(start >> 12) & 0xff];
+
+ // find end
+ cpt_e = cpt;
+ for (end_b = start + 0x1000; end_b < end; end_b += 0x1000) {
+ if ((end_b & 0x000ff000) == 0) {
+ cpt_e = __va(arm_pgd[end_b >> 20] & 0xfffffc00);
+ if ((arm_pgd[end_b >> 20] ^ desc1) & 0x3ff)
+ break;
+ }
+
+ // assume small pages
+ if ((cpt_e[(end_b >> 12) & 0xff] ^ desc2) & 0xfff)
+ break;
+ }
+
+ switch (desc2 & 3) {
+ case 0:
+ sprintf(buf, "l2_fault");
+ goto do_output;
+ case 1:
+ tex_cb = ((desc2 >> 2) & 0x03) | ((desc2 >> 10) & 0x1c);
+ s = (desc2 >> 10) & 1;
+ xn = (desc2 >> 15) & 1;
+ ap = ((desc2 >> 4) & 3) | ((desc2 >> 7) & 4);
+ break;
+ case 2:
+ case 3:
+ tex_cb = ((desc2 >> 2) & 0x03) | ((desc2 >> 4) & 0x1c);
+ s = (desc2 >> 10) & 1;
+ xn = desc2 & 1;
+ ap = ((desc2 >> 4) & 3) | ((desc2 >> 7) & 4);
+ break;
+ }
+
+do_tex_cb:
+ if (control & (1 << 28)) { // TEX remap
+ // S (shareable) bit remapping
+ char s_normal[2] = { (prrr >> 18) & 1, (prrr >> 19) & 1 };
+ char s_device[2] = { (prrr >> 16) & 1, (prrr >> 17) & 1 };
+
+ buf[0] = 0;
+ tex_cb &= 7;
+ type = (prrr >> tex_cb * 2) & 3;
+ switch (type) {
+ case 0:
+ sprintf(buf, "strongly-ordered");
+ break;
+ case 1:
+ sprintf(buf, "device");
+ s = s_device[s];
+ break;
+ case 3:
+ sprintf(buf, "reserved/normal");
+ case 2:
+ s = s_normal[s];
+ sprintf(buf + strlen(buf), "inner-%s-outer-%s",
+ cache_attrs4[(nmrr >> tex_cb * 2) & 3],
+ cache_attrs4[(nmrr >> (tex_cb * 2 + 16)) & 3]);
+ }
+ }
+ else if (tex_cb & 0x10) { // TEX[2] set
+ sprintf(buf, "inner-%s-outer-%s",
+ cache_attrs4[tex_cb & 3], cache_attrs4[(tex_cb >> 2) & 3]);
+ }
+ else {
+ switch (tex_cb) {
+ case 0x00: sprintf(buf, "strongly-ordered"); s = 1; break;
+ case 0x01: sprintf(buf, "shareable-device"); s = 1; break;
+ case 0x02: sprintf(buf, "inner-outer-WT-noWA"); break;
+ case 0x03: sprintf(buf, "inner-outer-WB-noWA"); break;
+ case 0x04: sprintf(buf, "inner-outer-non-cacheable"); break;
+ case 0x06: sprintf(buf, "implementation-defined"); break;
+ case 0x07: sprintf(buf, "inner-outer-WB-WA"); break;
+ case 0x08: sprintf(buf, "non-shareable-device"); s = 0; break;
+ default: sprintf(buf, "reserved"); break;
+ }
+ }
+
+ if (s)
+ sprintf(buf + strlen(buf), "-shareable");
+
+do_output:
+ // use user permissions here
+ if (control & (1 << 29)) // AFE
+ sprintf(rw, "%c%c", (ap & 2) ? 'r' : '-',
+ ((ap & 2) && !(ap & 4)) ? 'w' : '-');
+ else
+ sprintf(rw, "%c%c", (ap & 2) ? 'r' : '-',
+ (ap == 3) ? 'w' : '-');
+
+ seq_printf(m, "%08lx-%08lx %s%c%c%c %-28s %n",
+ start, end_b,
+ rw,
+ xn ? '-' : 'x',
+ flags & VM_MAYSHARE ? 's' : 'p',
+ desc_type,
+ buf, &len);
+
+ if (file) {
+ pad_len_spaces(m, len);
+ seq_path(m, &file->f_path, "\n");
+ } else if (name) {
+ pad_len_spaces(m, len);
+ seq_puts(m, name);
+ }
+ seq_putc(m, '\n');
+
+ start = end_b;
+ }
+}
+#endif
+
static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
{
struct mm_struct *mm = vma->vm_mm;
.release = seq_release_private,
};
+#ifdef __arm__
+static int show_armv7_map(struct seq_file *m, void *v)
+{
+ struct vm_area_struct *vma = v;
+ struct proc_maps_private *priv = m->private;
+ struct task_struct *task = priv->task;
+
+ show_arm_map_vma(m, vma);
+
+ if (m->count < m->size) /* vma is copied successfully */
+ m->version = (vma != get_gate_vma(task->mm))
+ ? vma->vm_start : 0;
+ return 0;
+}
+
+static const struct seq_operations proc_pid_armv7_maps_op = {
+ .start = m_start,
+ .next = m_next,
+ .stop = m_stop,
+ .show = show_armv7_map
+};
+#endif
+
+static int armv7_maps_open(struct inode *inode, struct file *file)
+{
+ return do_maps_open(inode, file, &proc_pid_armv7_maps_op);
+}
+
+const struct file_operations proc_armv7_maps_operations = {
+ .open = armv7_maps_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release_private,
+};
+
/*
* Proportional Set Size(PSS): my share of RSS.
*
} else {
spin_unlock(&walk->mm->page_table_lock);
}
+
+ if (pmd_trans_unstable(pmd))
+ return 0;
/*
* The mmap_sem held all the way back in m_start() is what
* keeps khugepaged out of here and from collapsing things
struct page *page;
split_huge_page_pmd(walk->mm, pmd);
+ if (pmd_trans_unstable(pmd))
+ return 0;
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
for (; addr != end; pte++, addr += PAGE_SIZE) {
if (!page)
continue;
+ if (PageReserved(page))
+ continue;
+
/* Clear accessed and referenced bits. */
ptep_test_and_clear_young(vma, addr, pte);
ClearPageReferenced(page);
};
struct pagemapread {
- int pos, len;
+ int pos, len; /* units: PM_ENTRY_BYTES, not bytes */
u64 *buffer;
+ bool show_pfn;
};
#define PM_ENTRY_BYTES sizeof(u64)
return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT);
}
-static u64 pte_to_pagemap_entry(pte_t pte)
+static u64 pte_to_pagemap_entry(struct pagemapread *pm, pte_t pte)
{
u64 pme = 0;
if (is_swap_pte(pte))
pme = PM_PFRAME(swap_pte_to_pagemap_entry(pte))
| PM_PSHIFT(PAGE_SHIFT) | PM_SWAP;
else if (pte_present(pte))
- pme = PM_PFRAME(pte_pfn(pte))
+ pme = (pm->show_pfn ? PM_PFRAME(pte_pfn(pte)) : 0)
| PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT;
return pme;
}
int err = 0;
split_huge_page_pmd(walk->mm, pmd);
+ if (pmd_trans_unstable(pmd))
+ return 0;
/* find the first VMA at or above 'addr' */
vma = find_vma(walk->mm, addr);
if (vma && (vma->vm_start <= addr) &&
!is_vm_hugetlb_page(vma)) {
pte = pte_offset_map(pmd, addr);
- pfn = pte_to_pagemap_entry(*pte);
+ pfn = pte_to_pagemap_entry(pm, *pte);
/* unmap before userspace copy */
pte_unmap(pte);
}
}
#ifdef CONFIG_HUGETLB_PAGE
-static u64 huge_pte_to_pagemap_entry(pte_t pte, int offset)
+static u64 huge_pte_to_pagemap_entry(struct pagemapread *pm, pte_t pte, int offset)
{
u64 pme = 0;
if (pte_present(pte))
- pme = PM_PFRAME(pte_pfn(pte) + offset)
+ pme = (pm->show_pfn ? PM_PFRAME(pte_pfn(pte) + offset) : 0)
| PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT;
return pme;
}
for (; addr != end; addr += PAGE_SIZE) {
int offset = (addr & ~hmask) >> PAGE_SHIFT;
- pfn = huge_pte_to_pagemap_entry(*pte, offset);
+ pfn = huge_pte_to_pagemap_entry(pm, *pte, offset);
err = add_to_pagemap(addr, pfn, pm);
if (err)
return err;
if (!count)
goto out_task;
- pm.len = PM_ENTRY_BYTES * (PAGEMAP_WALK_SIZE >> PAGE_SHIFT);
- pm.buffer = kmalloc(pm.len, GFP_TEMPORARY);
+ /* do not disclose physical addresses: attack vector */
+ pm.show_pfn = !security_capable(&init_user_ns, file->f_cred,
+ CAP_SYS_ADMIN);
+
+ pm.len = (PAGEMAP_WALK_SIZE >> PAGE_SHIFT);
+ pm.buffer = kmalloc(pm.len * PM_ENTRY_BYTES, GFP_TEMPORARY);
ret = -ENOMEM;
if (!pm.buffer)
goto out_task;
spin_unlock(&walk->mm->page_table_lock);
}
+ if (pmd_trans_unstable(pmd))
+ return 0;
orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
do {
struct page *page = can_gather_numa_stats(*pte, md->vma, addr);