Skip to content

Commit

Permalink
Merge branch 'mm-readonly-for-linus' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/tip/tip

Pull read-only kernel memory updates from Ingo Molnar:
 "This tree adds two (security related) enhancements to the kernel's
  handling of read-only kernel memory:

   - extend read-only kernel memory to a new class of formerly writable
     kernel data: 'post-init read-only memory' via the __ro_after_init
     attribute, and mark the ARM and x86 vDSO as such read-only memory.

     This kind of attribute can be used for data that requires a once
     per bootup initialization sequence, but is otherwise never modified
     after that point.

     This feature was based on the work by PaX Team and Brad Spengler.

     (by Kees Cook, the ARM vDSO bits by David Brown.)

   - make CONFIG_DEBUG_RODATA always enabled on x86 and remove the
     Kconfig option.  This simplifies the kernel and also signals that
     read-only memory is the default model and a first-class citizen.
     (Kees Cook)"

* 'mm-readonly-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  ARM/vdso: Mark the vDSO code read-only after init
  x86/vdso: Mark the vDSO code read-only after init
  lkdtm: Verify that '__ro_after_init' works correctly
  arch: Introduce post-init read-only memory
  x86/mm: Always enable CONFIG_DEBUG_RODATA and remove the Kconfig option
  mm/init: Add 'rodata=off' boot cmdline parameter to disable read-only kernel mappings
  asm-generic: Consolidate mark_rodata_ro()
  • Loading branch information
torvalds committed Mar 14, 2016
2 parents 5ec9424 + 11bf9b8 commit d09e356
Show file tree
Hide file tree
Showing 26 changed files with 103 additions and 84 deletions.
4 changes: 4 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3491,6 +3491,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.

ro [KNL] Mount root device read-only on boot

rodata= [KNL]
on Mark read-only kernel memory as read-only (default).
off Leave read-only kernel memory writable for debugging.

root= [KNL] Root filesystem
See name_to_dev_t comment in init/do_mounts.c.

Expand Down
1 change: 0 additions & 1 deletion arch/arm/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,6 @@ static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
#endif

#ifdef CONFIG_DEBUG_RODATA
void mark_rodata_ro(void);
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
#else
Expand Down
3 changes: 1 addition & 2 deletions arch/arm/vdso/vdso.S
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
#include <linux/const.h>
#include <asm/page.h>

__PAGE_ALIGNED_DATA

.globl vdso_start, vdso_end
.section .data..ro_after_init
.balign PAGE_SIZE
vdso_start:
.incbin "arch/arm/vdso/vdso.so"
Expand Down
4 changes: 0 additions & 4 deletions arch/arm64/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,4 @@ int set_memory_rw(unsigned long addr, int numpages);
int set_memory_x(unsigned long addr, int numpages);
int set_memory_nx(unsigned long addr, int numpages);

#ifdef CONFIG_DEBUG_RODATA
void mark_rodata_ro(void);
#endif

#endif
3 changes: 3 additions & 0 deletions arch/parisc/include/asm/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

#define __read_mostly __attribute__((__section__(".data..read_mostly")))

/* Read-only memory is marked before mark_rodata_ro() is called. */
#define __ro_after_init __read_mostly

void parisc_cache_init(void); /* initializes cache-flushing */
void disable_sr_hashing_asm(int); /* low level support for above */
void disable_sr_hashing(void); /* turns off space register hashing */
Expand Down
4 changes: 0 additions & 4 deletions arch/parisc/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vma
}
}

#ifdef CONFIG_DEBUG_RODATA
void mark_rodata_ro(void);
#endif

#include <asm/kmap_types.h>

#define ARCH_HAS_KMAP
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ config ARCH_SUPPORTS_UPROBES
config FIX_EARLYCON_MEM
def_bool y

config DEBUG_RODATA
def_bool y

config PGTABLE_LEVELS
int
default 4 if X86_64
Expand Down
18 changes: 3 additions & 15 deletions arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -74,28 +74,16 @@ config EFI_PGT_DUMP
issues with the mapping of the EFI runtime regions into that
table.

config DEBUG_RODATA
bool "Write protect kernel read-only data structures"
default y
depends on DEBUG_KERNEL
---help---
Mark the kernel read-only data as write-protected in the pagetables,
in order to catch accidental (and incorrect) writes to such const
data. This is recommended so that we can catch kernel bugs sooner.
If in doubt, say "Y".

config DEBUG_RODATA_TEST
bool "Testcase for the DEBUG_RODATA feature"
depends on DEBUG_RODATA
bool "Testcase for the marking rodata read-only"
default y
---help---
This option enables a testcase for the DEBUG_RODATA
feature as well as for the change_page_attr() infrastructure.
This option enables a testcase for the setting rodata read-only
as well as for the change_page_attr() infrastructure.
If in doubt, say "N"

config DEBUG_WX
bool "Warn on W+X mappings at boot"
depends on DEBUG_RODATA
select X86_PTDUMP_CORE
---help---
Generate a warning if any W+X mappings are found at boot.
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/entry/vdso/vdso2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
fprintf(outfile, "#include <asm/vdso.h>\n");
fprintf(outfile, "\n");
fprintf(outfile,
"static unsigned char raw_data[%lu] __page_aligned_data = {",
"static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
mapping_size);
for (j = 0; j < stripped_len; j++) {
if (j % 10 == 0)
Expand Down
6 changes: 0 additions & 6 deletions arch/x86/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,10 @@ void clflush_cache_range(void *addr, unsigned int size);

#define mmio_flush_range(addr, size) clflush_cache_range(addr, size)

#ifdef CONFIG_DEBUG_RODATA
void mark_rodata_ro(void);
extern const int rodata_test_data;
extern int kernel_set_to_readonly;
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
#else
static inline void set_kernel_text_rw(void) { }
static inline void set_kernel_text_ro(void) { }
#endif

#ifdef CONFIG_DEBUG_RODATA_TEST
int rodata_test(void);
Expand Down
7 changes: 0 additions & 7 deletions arch/x86/include/asm/kvm_para.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,8 @@ static inline bool kvm_check_and_clear_guest_paused(void)
}
#endif /* CONFIG_KVM_GUEST */

#ifdef CONFIG_DEBUG_RODATA
#define KVM_HYPERCALL \
ALTERNATIVE(".byte 0x0f,0x01,0xc1", ".byte 0x0f,0x01,0xd9", X86_FEATURE_VMMCALL)
#else
/* On AMD processors, vmcall will generate a trap that we will
* then rewrite to the appropriate instruction.
*/
#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
#endif

/* For KVM hypercalls, a three-byte sequence of either the vmcall or the vmmcall
* instruction. The hypervisor may replace it with something else but only the
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/include/asm/sections.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
extern char __brk_base[], __brk_limit[];
extern struct exception_table_entry __stop___ex_table[];

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
#if defined(CONFIG_X86_64)
extern char __end_rodata_hpage_align[];
#endif

Expand Down
6 changes: 3 additions & 3 deletions arch/x86/kernel/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ within(unsigned long addr, unsigned long start, unsigned long end)
static unsigned long text_ip_addr(unsigned long ip)
{
/*
* On x86_64, kernel text mappings are mapped read-only with
* CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
* of the kernel text mapping to modify the kernel text.
* On x86_64, kernel text mappings are mapped read-only, so we use
* the kernel identity mapping instead of the kernel text mapping
* to modify the kernel text.
*
* For 32bit kernels, these mappings are same and we can use
* kernel identity mapping to modify code.
Expand Down
8 changes: 2 additions & 6 deletions arch/x86/kernel/kgdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -750,9 +750,7 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{
int err;
#ifdef CONFIG_DEBUG_RODATA
char opc[BREAK_INSTR_SIZE];
#endif /* CONFIG_DEBUG_RODATA */

bpt->type = BP_BREAKPOINT;
err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
Expand All @@ -761,7 +759,6 @@ int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
return err;
err = probe_kernel_write((char *)bpt->bpt_addr,
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
#ifdef CONFIG_DEBUG_RODATA
if (!err)
return err;
/*
Expand All @@ -778,13 +775,12 @@ int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE))
return -EINVAL;
bpt->type = BP_POKE_BREAKPOINT;
#endif /* CONFIG_DEBUG_RODATA */

return err;
}

int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{
#ifdef CONFIG_DEBUG_RODATA
int err;
char opc[BREAK_INSTR_SIZE];

Expand All @@ -801,8 +797,8 @@ int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE))
goto knl_write;
return err;

knl_write:
#endif /* CONFIG_DEBUG_RODATA */
return probe_kernel_write((char *)bpt->bpt_addr,
(char *)bpt->saved_instr, BREAK_INSTR_SIZE);
}
Expand Down
2 changes: 0 additions & 2 deletions arch/x86/kernel/test_nx.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ static int test_NX(void)
* by the error message
*/

#ifdef CONFIG_DEBUG_RODATA
/* Test 3: Check if the .rodata section is executable */
if (rodata_test_data != 0xC3) {
printk(KERN_ERR "test_nx: .rodata marker has invalid value\n");
Expand All @@ -151,7 +150,6 @@ static int test_NX(void)
printk(KERN_ERR "test_nx: .rodata section is executable\n");
ret = -ENODEV;
}
#endif

#if 0
/* Test 4: Check if the .data section of a module is executable */
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/test_rodata.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,5 @@ int rodata_test(void)
}

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Testcase for the DEBUG_RODATA infrastructure");
MODULE_DESCRIPTION("Testcase for marking rodata as read-only");
MODULE_AUTHOR("Arjan van de Ven <[email protected]>");
25 changes: 11 additions & 14 deletions arch/x86/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,28 @@ ENTRY(phys_startup_64)
jiffies_64 = jiffies;
#endif

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
#if defined(CONFIG_X86_64)
/*
* On 64-bit, align RODATA to 2MB so that even with CONFIG_DEBUG_RODATA
* we retain large page mappings for boundaries spanning kernel text, rodata
* and data sections.
* On 64-bit, align RODATA to 2MB so we retain large page mappings for
* boundaries spanning kernel text, rodata and data sections.
*
* However, kernel identity mappings will have different RWX permissions
* to the pages mapping to text and to the pages padding (which are freed) the
* text section. Hence kernel identity mappings will be broken to smaller
* pages. For 64-bit, kernel text and kernel identity mappings are different,
* so we can enable protection checks that come with CONFIG_DEBUG_RODATA,
* as well as retain 2MB large page mappings for kernel text.
* so we can enable protection checks as well as retain 2MB large page
* mappings for kernel text.
*/
#define X64_ALIGN_DEBUG_RODATA_BEGIN . = ALIGN(HPAGE_SIZE);
#define X64_ALIGN_RODATA_BEGIN . = ALIGN(HPAGE_SIZE);

#define X64_ALIGN_DEBUG_RODATA_END \
#define X64_ALIGN_RODATA_END \
. = ALIGN(HPAGE_SIZE); \
__end_rodata_hpage_align = .;

#else

#define X64_ALIGN_DEBUG_RODATA_BEGIN
#define X64_ALIGN_DEBUG_RODATA_END
#define X64_ALIGN_RODATA_BEGIN
#define X64_ALIGN_RODATA_END

#endif

Expand Down Expand Up @@ -112,13 +111,11 @@ SECTIONS

EXCEPTION_TABLE(16) :text = 0x9090

#if defined(CONFIG_DEBUG_RODATA)
/* .text should occupy whole number of pages */
. = ALIGN(PAGE_SIZE);
#endif
X64_ALIGN_DEBUG_RODATA_BEGIN
X64_ALIGN_RODATA_BEGIN
RO_DATA(PAGE_SIZE)
X64_ALIGN_DEBUG_RODATA_END
X64_ALIGN_RODATA_END

/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
Expand Down
3 changes: 0 additions & 3 deletions arch/x86/mm/init_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,6 @@ static noinline int do_test_wp_bit(void)
return flag;
}

#ifdef CONFIG_DEBUG_RODATA
const int rodata_test_data = 0xC3;
EXPORT_SYMBOL_GPL(rodata_test_data);

Expand Down Expand Up @@ -960,5 +959,3 @@ void mark_rodata_ro(void)
if (__supported_pte_mask & _PAGE_NX)
debug_checkwx();
}
#endif

3 changes: 0 additions & 3 deletions arch/x86/mm/init_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,6 @@ void __init mem_init(void)
mem_init_print_info(NULL);
}

#ifdef CONFIG_DEBUG_RODATA
const int rodata_test_data = 0xC3;
EXPORT_SYMBOL_GPL(rodata_test_data);

Expand Down Expand Up @@ -1166,8 +1165,6 @@ void mark_rodata_ro(void)
debug_checkwx();
}

#endif

int kern_addr_valid(unsigned long addr)
{
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/mm/pageattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
__pa_symbol(__end_rodata) >> PAGE_SHIFT))
pgprot_val(forbidden) |= _PAGE_RW;

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
#if defined(CONFIG_X86_64)
/*
* Once the kernel maps the text as RO (kernel_set_to_readonly is set),
* kernel text mappings for the large page aligned text, rodata sections
Expand Down
29 changes: 26 additions & 3 deletions drivers/misc/lkdtm.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ enum ctype {
CT_EXEC_USERSPACE,
CT_ACCESS_USERSPACE,
CT_WRITE_RO,
CT_WRITE_RO_AFTER_INIT,
CT_WRITE_KERN,
};

Expand Down Expand Up @@ -140,6 +141,7 @@ static char* cp_type[] = {
"EXEC_USERSPACE",
"ACCESS_USERSPACE",
"WRITE_RO",
"WRITE_RO_AFTER_INIT",
"WRITE_KERN",
};

Expand All @@ -162,6 +164,7 @@ static DEFINE_SPINLOCK(lock_me_up);
static u8 data_area[EXEC_SIZE];

static const unsigned long rodata = 0xAA55AA55;
static unsigned long ro_after_init __ro_after_init = 0x55AA5500;

module_param(recur_count, int, 0644);
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
Expand Down Expand Up @@ -503,11 +506,28 @@ static void lkdtm_do_action(enum ctype which)
break;
}
case CT_WRITE_RO: {
unsigned long *ptr;
/* Explicitly cast away "const" for the test. */
unsigned long *ptr = (unsigned long *)&rodata;

ptr = (unsigned long *)&rodata;
pr_info("attempting bad rodata write at %p\n", ptr);
*ptr ^= 0xabcd1234;

pr_info("attempting bad write at %p\n", ptr);
break;
}
case CT_WRITE_RO_AFTER_INIT: {
unsigned long *ptr = &ro_after_init;

/*
* Verify we were written to during init. Since an Oops
* is considered a "success", a failure is to just skip the
* real test.
*/
if ((*ptr & 0xAA) != 0xAA) {
pr_info("%p was NOT written during init!?\n", ptr);
break;
}

pr_info("attempting bad ro_after_init write at %p\n", ptr);
*ptr ^= 0xabcd1234;

break;
Expand Down Expand Up @@ -817,6 +837,9 @@ static int __init lkdtm_module_init(void)
int n_debugfs_entries = 1; /* Assume only the direct entry */
int i;

/* Make sure we can write to __ro_after_init values during __init */
ro_after_init |= 0xAA;

/* Register debugfs interface */
lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
if (!lkdtm_debugfs_root) {
Expand Down
Loading

0 comments on commit d09e356

Please sign in to comment.