arm: mm: program ptes for access restriction

CONFIG_RODATA allows strict kernel mapping permissions to
be followed and accordingly maps regions as read-only,
not-executable etc. correspondingly. CONFIG_RODATA however
assumes all memory regions to be SECTION_SIZE aligned and
section mapped for performance reasons.

With CONFIG_FORCE_PAGES, we force all kernel mappings as
page mapped thus breaking CONFIG_RODATA.

Provide provision to apply permissions at page (pte)
level, if CONFIG_RODATA does not find section mapping.

Change-Id: I8dbf5c3741836bc63a231d8a471cf0306662993b
Signed-off-by: Shiraz Hashim <shashim@codeaurora.org>
Signed-off-by: Charan Teja Reddy <charante@codeaurora.org>
Signed-off-by: Vijayanand Jitta <vjitta@codeaurora.org>
Signed-off-by: Qingqing Zhou <qqzhou@codeaurora.org>
This commit is contained in:
Shiraz Hashim 2015-10-07 18:59:01 +05:30 • committed by Gerrit - the friendly Code Review server
commit 629471283e

View file

@ -511,6 +511,9 @@ struct section_perm {
pmdval_t mask;
pmdval_t prot;
pmdval_t clear;
pteval_t ptemask;
pteval_t pteprot;
pteval_t pteclear;
};
/* First section-aligned location at or after __start_rodata. */
@ -524,6 +527,8 @@ static struct section_perm nx_perms[] = {
.end = (unsigned long)_stext,
.mask = ~PMD_SECT_XN,
.prot = PMD_SECT_XN,
.ptemask = ~L_PTE_XN,
.pteprot = L_PTE_XN,
},
/* Make init RW (set NX). */
{
@ -532,6 +537,8 @@ static struct section_perm nx_perms[] = {
.end = (unsigned long)_sdata,
.mask = ~PMD_SECT_XN,
.prot = PMD_SECT_XN,
.ptemask = ~L_PTE_XN,
.pteprot = L_PTE_XN,
},
/* Make rodata NX (set RO in ro_perms below). */
{
@ -540,6 +547,8 @@ static struct section_perm nx_perms[] = {
.end = (unsigned long)__init_begin,
.mask = ~PMD_SECT_XN,
.prot = PMD_SECT_XN,
.ptemask = ~L_PTE_XN,
.pteprot = L_PTE_XN,
},
};
@ -557,6 +566,8 @@ static struct section_perm ro_perms[] = {
.prot = PMD_SECT_APX | PMD_SECT_AP_WRITE,
.clear = PMD_SECT_AP_WRITE,
#endif
.ptemask = ~L_PTE_RDONLY,
.pteprot = L_PTE_RDONLY,
},
};
@ -565,6 +576,34 @@ static struct section_perm ro_perms[] = {
* copied into each mm). During startup, this is the init_mm. Is only
* safe to be called with preemption disabled, as under stop_machine().
*/
struct pte_data {
pteval_t mask;
pteval_t val;
};
static int __pte_update(pte_t *ptep, unsigned long addr, void *d)
{
struct pte_data *data = d;
pte_t pte = *ptep;
pte = __pte((pte_val(*ptep) & data->mask) | data->val);
set_pte_ext(ptep, pte, 0);
return 0;
}
static inline void pte_update(unsigned long addr, pteval_t mask,
pteval_t prot, struct mm_struct *mm)
{
struct pte_data data;
data.mask = mask;
data.val = prot;
apply_to_page_range(mm, addr, SECTION_SIZE, __pte_update, &data);
flush_tlb_kernel_range(addr, addr + SECTION_SIZE);
}
static inline void section_update(unsigned long addr, pmdval_t mask,
pmdval_t prot, struct mm_struct *mm)
{
@ -613,11 +652,21 @@ void set_section_perms(struct section_perm *perms, int n, bool set,
for (addr = perms[i].start;
addr < perms[i].end;
addr += SECTION_SIZE)
section_update(addr, perms[i].mask,
set ? perms[i].prot : perms[i].clear, mm);
}
addr += SECTION_SIZE) {
pmd_t *pmd;
pmd = pmd_offset(pud_offset(pgd_offset(mm, addr),
addr), addr);
if (pmd_bad(*pmd))
section_update(addr, perms[i].mask,
set ? perms[i].prot : perms[i].clear,
mm);
else
pte_update(addr, perms[i].ptemask,
set ? perms[i].pteprot : perms[i].pteclear,
mm);
}
}
}
/**