Print Kernel's Page Table Entries

Dumping page table entries of a process in Linux

I think /proc/pid/pagemap and /proc/pid/maps contain this info, but I am not aware of any tool dumping them in a more meaningful format.

You can always write it yourself using the kernel doc:

http://www.kernel.org/doc/Documentation/vm/pagemap.txt

Page Table Walk in Linux

I fixed the problem thanks to TonyTannous. The page table entry doesn't contain just the physical address, but also some other bits used for access rights and the like. The physical address can be obtained by masking it with PTE_PFN_MASK:

    addr = ptep->pte & PTE_PFN_MASK

I can then dereference it with __va.

Override a page table entry with another on a x64 Ubuntu 14.04

I am writing this answer hoping that this will prove useful for someone in the future.

I have managed to modify the page tables for the virtual addresses of two variables foo and bar so the mm will see them as residing in the same physical page.

The code that does this is below:

#include <linux/module.h>                                                                                                                                                                                      
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/thread_info.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/tlbflush.h>
#include <asm/page.h>
#include <asm/pgtable.h>

pgd_t *foo_pgd, *bar_pgd;
pud_t *foo_pud, *bar_pud;
pmd_t *foo_pmd, *bar_pmd;
pte_t *foo_ptep, *bar_ptep, foo_pte, bar_pte;

/* We will step through the page tables until we reach the
* PTE for both foo and bar */

/* Get PGD */
foo_pgd = pgd_offset(current->mm, foo_address);
if (pgd_none(*foo_pgd) || pgd_bad(*foo_pgd))
return -EINVAL;

bar_pgd = pgd_offset(current->mm, bar_address);
if (pgd_none(*bar_pgd) || pgd_bad(*bar_pgd))
return -EINVAL;

/* Get PUD */
foo_pud = pud_offset(foo_pgd, foo_address);
if (pud_none(*foo_pud) || pud_bad(*foo_pud))
return -EINVAL;

bar_pud = pud_offset(bar_pgd, bar_address);
if (pud_none(*bar_pud) || pud_bad(*bar_pud))
return -EINVAL;

/* Get PMD */
foo_pmd = pmd_offset(foo_pud, foo_address);
if (pmd_none(*foo_pmd) || pmd_bad(*foo_pmd))
return -EINVAL;

bar_pmd = pmd_offset(bar_pud, bar_address);
if (pmd_none(*bar_pmd) || pmd_bad(*bar_pmd))
return -EINVAL;

/* Get PTE */
spin_lock(current->mm->page_table_lock);
foo_ptep = pte_offset_map(foo_pmd, foo_address);
if (!pte_present(*foo_ptep))
return -EINVAL;

bar_ptep = pte_offset_map(bar_pmd, bar_address);
if (!pte_present(*bar_ptep))
return -EINVAL;

/* Trick foo into thinking he resides in the same page as bar */
*foo_ptep = *bar_ptep;

spin_unlock(current->mm->page_table_lock);

/* We need to flush the tlb afterwards */
__native_flush_tlb();

This may not be as robust as what @GilHamilton had in mind, but this got the job done for me.

I hope this will prove to be useful!

Eduard



Related Topics



Leave a reply



Submit