[patch 01/02] Debug option to write-protect rodata: change_page_attr fixes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I've been working on a patch that turns the kernel's .rodata section to be
actually read only, eg any write attempts to it cause a segmentation fault.
During this work I found a bug in the change_page_attr() code on x86-64, and
this patch 1 is the bugfix for that. Patch 2 will be the actual introduction
of the readonly option.


The bug fixed here is the following: when splitting a 2Mb PSE page that also
happened to contain kernel text, the split PMD would get the NX bit set
eventhough the individual pte entries for the 4Kb pages would not. This
causes problems still since this NX bit overrides in practice the sub bits.
The solution I've chosen is I think the right one: On splitting a 2Mb page
in the change_page_attr() code, inherit the existing pagetable permissions
exactly, for both the PMD page and all the 4Kb pte entries (with the
exception of the one that you wanted to change, but the code already did
that part correct).


Signed-off-by: Arjan van de Ven <[email protected]>

diff -purN linux-2.6.14/arch/x86_64/mm/pageattr.c linux-2.6.14-fordiff/arch/x86_64/mm/pageattr.c
--- linux-2.6.14/arch/x86_64/mm/pageattr.c	2005-10-28 02:02:08.000000000 +0200
+++ linux-2.6.14-fordiff/arch/x86_64/mm/pageattr.c	2005-11-05 18:27:07.000000000 +0100
@@ -128,6 +131,7 @@ __change_page_attr(unsigned long address
 	pte_t *kpte; 
 	struct page *kpte_page;
 	unsigned kpte_flags;
+	pgprot_t ref_prot2;
 	kpte = lookup_address(address);
 	if (!kpte) return 0;
 	kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK);
@@ -140,10 +144,14 @@ __change_page_attr(unsigned long address
  			 * split_large_page will take the reference for this change_page_attr
  			 * on the split page.
  			 */
-			struct page *split = split_large_page(address, prot, ref_prot); 
+
+			struct page *split;
+			ref_prot2 = __pgprot(pgprot_val(pte_pgprot(*lookup_address(address))) & ~(1<<_PAGE_BIT_PSE));
+
+			split = split_large_page(address, prot, ref_prot2); 
 			if (!split)
 				return -ENOMEM;
-			set_pte(kpte,mk_pte(split, ref_prot));
+			set_pte(kpte,mk_pte(split, ref_prot2));
 			kpte_page = split;
 		}	
 		get_page(kpte_page);
diff -purN linux-2.6.14/include/asm-x86_64/pgtable.h linux-2.6.14-fordiff/include/asm-x86_64/pgtable.h
--- linux-2.6.14/include/asm-x86_64/pgtable.h	2005-11-03 18:49:01.000000000 +0100
+++ linux-2.6.14-fordiff/include/asm-x86_64/pgtable.h	2005-11-05 13:20:44.000000000 +0100
@@ -119,6 +119,8 @@ static inline pte_t ptep_get_and_clear_f
 
 #define pte_same(a, b)		((a).pte == (b).pte)
 
+#define pte_pgprot(a)	(__pgprot((a).pte & ~(PHYSICAL_PAGE_MASK)))
+
 #define PMD_SIZE	(1UL << PMD_SHIFT)
 #define PMD_MASK	(~(PMD_SIZE-1))
 #define PUD_SIZE	(1UL << PUD_SHIFT)

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux