Skip to content

Commit 4f00b90

Browse files
committed
Merge branch 'x86-security-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'x86-security-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: module: Move RO/NX module protection to after ftrace module update x86: Resume trampoline must be executable x86: Add RO/NX protection for loadable kernel modules x86: Add NX protection for kernel data x86: Fix improper large page preservation
2 parents b4c6e2e + 94462ad commit 4f00b90

File tree

10 files changed

+266
-18
lines changed

10 files changed

+266
-18
lines changed

arch/x86/Kconfig.debug

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ config DEBUG_RODATA_TEST
117117
feature as well as for the change_page_attr() infrastructure.
118118
If in doubt, say "N"
119119

120+
config DEBUG_SET_MODULE_RONX
121+
bool "Set loadable kernel module data as NX and text as RO"
122+
depends on MODULES
123+
---help---
124+
This option helps catch unintended modifications to loadable
125+
kernel module's text and read-only data. It also prevents execution
126+
of module data. Such protection may interfere with run-time code
127+
patching and dynamic kernel tracing - and they might also protect
128+
against certain classes of kernel exploits.
129+
If in doubt, say "N".
130+
120131
config DEBUG_NX_TEST
121132
tristate "Testcase for the NX non-executable stack feature"
122133
depends on DEBUG_KERNEL && m

arch/x86/include/asm/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ extern unsigned long pci_mem_start;
6565

6666
#define PCIBIOS_MIN_CARDBUS_IO 0x4000
6767

68+
extern int pcibios_enabled;
6869
void pcibios_config_init(void);
6970
struct pci_bus *pcibios_scan_root(int bus);
7071

arch/x86/kernel/ftrace.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/sched.h>
2020
#include <linux/init.h>
2121
#include <linux/list.h>
22+
#include <linux/module.h>
2223

2324
#include <trace/syscall.h>
2425

@@ -49,13 +50,15 @@ static DEFINE_PER_CPU(int, save_modifying_code);
4950
int ftrace_arch_code_modify_prepare(void)
5051
{
5152
set_kernel_text_rw();
53+
set_all_modules_text_rw();
5254
modifying_code = 1;
5355
return 0;
5456
}
5557

5658
int ftrace_arch_code_modify_post_process(void)
5759
{
5860
modifying_code = 0;
61+
set_all_modules_text_ro();
5962
set_kernel_text_ro();
6063
return 0;
6164
}

arch/x86/kernel/vmlinux.lds.S

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jiffies_64 = jiffies;
6969

7070
PHDRS {
7171
text PT_LOAD FLAGS(5); /* R_E */
72-
data PT_LOAD FLAGS(7); /* RWE */
72+
data PT_LOAD FLAGS(6); /* RW_ */
7373
#ifdef CONFIG_X86_64
7474
user PT_LOAD FLAGS(5); /* R_E */
7575
#ifdef CONFIG_SMP
@@ -116,6 +116,10 @@ SECTIONS
116116

117117
EXCEPTION_TABLE(16) :text = 0x9090
118118

119+
#if defined(CONFIG_DEBUG_RODATA)
120+
/* .text should occupy whole number of pages */
121+
. = ALIGN(PAGE_SIZE);
122+
#endif
119123
X64_ALIGN_DEBUG_RODATA_BEGIN
120124
RO_DATA(PAGE_SIZE)
121125
X64_ALIGN_DEBUG_RODATA_END
@@ -335,7 +339,7 @@ SECTIONS
335339
__bss_start = .;
336340
*(.bss..page_aligned)
337341
*(.bss)
338-
. = ALIGN(4);
342+
. = ALIGN(PAGE_SIZE);
339343
__bss_stop = .;
340344
}
341345

arch/x86/mm/init.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,9 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
364364
/*
365365
* We just marked the kernel text read only above, now that
366366
* we are going to free part of that, we need to make that
367-
* writeable first.
367+
* writeable and non-executable first.
368368
*/
369+
set_memory_nx(begin, (end - begin) >> PAGE_SHIFT);
369370
set_memory_rw(begin, (end - begin) >> PAGE_SHIFT);
370371

371372
printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10);

arch/x86/mm/init_32.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
226226

227227
static inline int is_kernel_text(unsigned long addr)
228228
{
229-
if (addr >= PAGE_OFFSET && addr <= (unsigned long)__init_end)
229+
if (addr >= (unsigned long)_text && addr <= (unsigned long)__init_end)
230230
return 1;
231231
return 0;
232232
}
@@ -912,6 +912,23 @@ void set_kernel_text_ro(void)
912912
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
913913
}
914914

915+
static void mark_nxdata_nx(void)
916+
{
917+
/*
918+
* When this called, init has already been executed and released,
919+
* so everything past _etext sould be NX.
920+
*/
921+
unsigned long start = PFN_ALIGN(_etext);
922+
/*
923+
* This comes from is_kernel_text upper limit. Also HPAGE where used:
924+
*/
925+
unsigned long size = (((unsigned long)__init_end + HPAGE_SIZE) & HPAGE_MASK) - start;
926+
927+
if (__supported_pte_mask & _PAGE_NX)
928+
printk(KERN_INFO "NX-protecting the kernel data: %luk\n", size >> 10);
929+
set_pages_nx(virt_to_page(start), size >> PAGE_SHIFT);
930+
}
931+
915932
void mark_rodata_ro(void)
916933
{
917934
unsigned long start = PFN_ALIGN(_text);
@@ -946,6 +963,7 @@ void mark_rodata_ro(void)
946963
printk(KERN_INFO "Testing CPA: write protecting again\n");
947964
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
948965
#endif
966+
mark_nxdata_nx();
949967
}
950968
#endif
951969

arch/x86/mm/pageattr.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/pfn.h>
1414
#include <linux/percpu.h>
1515
#include <linux/gfp.h>
16+
#include <linux/pci.h>
1617

1718
#include <asm/e820.h>
1819
#include <asm/processor.h>
@@ -255,13 +256,16 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
255256
unsigned long pfn)
256257
{
257258
pgprot_t forbidden = __pgprot(0);
259+
pgprot_t required = __pgprot(0);
258260

259261
/*
260262
* The BIOS area between 640k and 1Mb needs to be executable for
261263
* PCI BIOS based config access (CONFIG_PCI_GOBIOS) support.
262264
*/
263-
if (within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT))
265+
#ifdef CONFIG_PCI_BIOS
266+
if (pcibios_enabled && within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT))
264267
pgprot_val(forbidden) |= _PAGE_NX;
268+
#endif
265269

266270
/*
267271
* The kernel text needs to be executable for obvious reasons
@@ -278,6 +282,12 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
278282
if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT,
279283
__pa((unsigned long)__end_rodata) >> PAGE_SHIFT))
280284
pgprot_val(forbidden) |= _PAGE_RW;
285+
/*
286+
* .data and .bss should always be writable.
287+
*/
288+
if (within(address, (unsigned long)_sdata, (unsigned long)_edata) ||
289+
within(address, (unsigned long)__bss_start, (unsigned long)__bss_stop))
290+
pgprot_val(required) |= _PAGE_RW;
281291

282292
#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
283293
/*
@@ -317,6 +327,7 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
317327
#endif
318328

319329
prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));
330+
prot = __pgprot(pgprot_val(prot) | pgprot_val(required));
320331

321332
return prot;
322333
}
@@ -393,7 +404,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
393404
{
394405
unsigned long nextpage_addr, numpages, pmask, psize, flags, addr, pfn;
395406
pte_t new_pte, old_pte, *tmp;
396-
pgprot_t old_prot, new_prot;
407+
pgprot_t old_prot, new_prot, req_prot;
397408
int i, do_split = 1;
398409
unsigned int level;
399410

@@ -438,10 +449,10 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
438449
* We are safe now. Check whether the new pgprot is the same:
439450
*/
440451
old_pte = *kpte;
441-
old_prot = new_prot = pte_pgprot(old_pte);
452+
old_prot = new_prot = req_prot = pte_pgprot(old_pte);
442453

443-
pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr);
444-
pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
454+
pgprot_val(req_prot) &= ~pgprot_val(cpa->mask_clr);
455+
pgprot_val(req_prot) |= pgprot_val(cpa->mask_set);
445456

446457
/*
447458
* old_pte points to the large page base address. So we need
@@ -450,17 +461,17 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
450461
pfn = pte_pfn(old_pte) + ((address & (psize - 1)) >> PAGE_SHIFT);
451462
cpa->pfn = pfn;
452463

453-
new_prot = static_protections(new_prot, address, pfn);
464+
new_prot = static_protections(req_prot, address, pfn);
454465

455466
/*
456467
* We need to check the full range, whether
457468
* static_protection() requires a different pgprot for one of
458469
* the pages in the range we try to preserve:
459470
*/
460-
addr = address + PAGE_SIZE;
461-
pfn++;
462-
for (i = 1; i < cpa->numpages; i++, addr += PAGE_SIZE, pfn++) {
463-
pgprot_t chk_prot = static_protections(new_prot, addr, pfn);
471+
addr = address & pmask;
472+
pfn = pte_pfn(old_pte);
473+
for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) {
474+
pgprot_t chk_prot = static_protections(req_prot, addr, pfn);
464475

465476
if (pgprot_val(chk_prot) != pgprot_val(new_prot))
466477
goto out_unlock;
@@ -483,7 +494,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address,
483494
* that we limited the number of possible pages already to
484495
* the number of pages in the large page.
485496
*/
486-
if (address == (nextpage_addr - psize) && cpa->numpages == numpages) {
497+
if (address == (address & pmask) && cpa->numpages == (psize >> PAGE_SHIFT)) {
487498
/*
488499
* The address is aligned and the number of pages
489500
* covers the full page.

arch/x86/pci/pcbios.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/uaccess.h>
1010
#include <asm/pci_x86.h>
1111
#include <asm/pci-functions.h>
12+
#include <asm/cacheflush.h>
1213

1314
/* BIOS32 signature: "_32_" */
1415
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
@@ -25,6 +26,27 @@
2526
#define PCIBIOS_HW_TYPE1_SPEC 0x10
2627
#define PCIBIOS_HW_TYPE2_SPEC 0x20
2728

29+
int pcibios_enabled;
30+
31+
/* According to the BIOS specification at:
32+
* http://members.datafast.net.au/dft0802/specs/bios21.pdf, we could
33+
* restrict the x zone to some pages and make it ro. But this may be
34+
* broken on some bios, complex to handle with static_protections.
35+
* We could make the 0xe0000-0x100000 range rox, but this can break
36+
* some ISA mapping.
37+
*
38+
* So we let's an rw and x hole when pcibios is used. This shouldn't
39+
* happen for modern system with mmconfig, and if you don't want it
40+
* you could disable pcibios...
41+
*/
42+
static inline void set_bios_x(void)
43+
{
44+
pcibios_enabled = 1;
45+
set_memory_x(PAGE_OFFSET + BIOS_BEGIN, (BIOS_END - BIOS_BEGIN) >> PAGE_SHIFT);
46+
if (__supported_pte_mask & _PAGE_NX)
47+
printk(KERN_INFO "PCI : PCI BIOS aera is rw and x. Use pci=nobios if you want it NX.\n");
48+
}
49+
2850
/*
2951
* This is the standard structure used to identify the entry point
3052
* to the BIOS32 Service Directory, as documented in
@@ -332,6 +354,7 @@ static struct pci_raw_ops * __devinit pci_find_bios(void)
332354
DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n",
333355
bios32_entry);
334356
bios32_indirect.address = bios32_entry + PAGE_OFFSET;
357+
set_bios_x();
335358
if (check_pcibios())
336359
return &pci_bios_access;
337360
}

include/linux/module.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ struct module
308308
/* The size of the executable code in each section. */
309309
unsigned int init_text_size, core_text_size;
310310

311+
/* Size of RO sections of the module (text+rodata) */
312+
unsigned int init_ro_size, core_ro_size;
313+
311314
/* Arch-specific module values */
312315
struct mod_arch_specific arch;
313316

@@ -672,7 +675,6 @@ static inline int module_get_iter_tracepoints(struct tracepoint_iter *iter)
672675
{
673676
return 0;
674677
}
675-
676678
#endif /* CONFIG_MODULES */
677679

678680
#ifdef CONFIG_SYSFS
@@ -687,6 +689,13 @@ extern int module_sysfs_initialized;
687689

688690
#define __MODULE_STRING(x) __stringify(x)
689691

692+
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
693+
extern void set_all_modules_text_rw(void);
694+
extern void set_all_modules_text_ro(void);
695+
#else
696+
static inline void set_all_modules_text_rw(void) { }
697+
static inline void set_all_modules_text_ro(void) { }
698+
#endif
690699

691700
#ifdef CONFIG_GENERIC_BUG
692701
void module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,

0 commit comments

Comments
 (0)