Skip to content

Commit

Permalink
ARM: 7487/1: mm: avoid setting nG bit for user mappings that aren't p…
Browse files Browse the repository at this point in the history
…resent

Swap entries are encoding in ptes such that !pte_present(pte) and
pte_file(pte). The remaining bits of the descriptor are used to identify
the swapfile and offset within it to the swap entry.

When writing such a pte for a user virtual address, set_pte_at
unconditionally sets the nG bit, which (in the case of LPAE) will
corrupt the swapfile offset and lead to a BUG:

[  140.494067] swap_free: Unused swap offset entry 000763b4
[  140.509989] BUG: Bad page map in process rs:main Q:Reg  pte:0ec76800 pmd:8f92e003

This patch fixes the problem by only setting the nG bit for user
mappings that are actually present.

Cc: <stable@vger.kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Will Deacon authored and Russell King committed Aug 11, 2012
1 parent 237ec6f commit 47f1204
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 18 deletions.
34 changes: 18 additions & 16 deletions arch/arm/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,18 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)

#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)

#define pte_none(pte) (!pte_val(pte))
#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
#define pte_special(pte) (0)

#define pte_present_user(pte) \
((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
(L_PTE_PRESENT | L_PTE_USER))

#if __LINUX_ARM_ARCH__ < 6
static inline void __sync_icache_dcache(pte_t pteval)
{
Expand All @@ -206,25 +218,15 @@ extern void __sync_icache_dcache(pte_t pteval);
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pteval)
{
if (addr >= TASK_SIZE)
set_pte_ext(ptep, pteval, 0);
else {
unsigned long ext = 0;

if (addr < TASK_SIZE && pte_present_user(pteval)) {
__sync_icache_dcache(pteval);
set_pte_ext(ptep, pteval, PTE_EXT_NG);
ext |= PTE_EXT_NG;
}
}

#define pte_none(pte) (!pte_val(pte))
#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
#define pte_special(pte) (0)

#define pte_present_user(pte) \
((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
(L_PTE_PRESENT | L_PTE_USER))
set_pte_ext(ptep, pteval, ext);
}

#define PTE_BIT_FUNC(fn,op) \
static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
Expand Down
2 changes: 0 additions & 2 deletions arch/arm/mm/flush.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ void __sync_icache_dcache(pte_t pteval)
struct page *page;
struct address_space *mapping;

if (!pte_present_user(pteval))
return;
if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
/* only flush non-aliasing VIPT caches for exec mappings */
return;
Expand Down

0 comments on commit 47f1204

Please sign in to comment.