mm/page_poisoning.c: allow for zero poisoning
authorLaura Abbott <labbott@fedoraproject.org>
Tue, 15 Mar 2016 21:56:30 +0000 (14:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Mar 2016 23:55:16 +0000 (16:55 -0700)
By default, page poisoning uses a poison value (0xaa) on free.  If this
is changed to 0, the page is not only sanitized but zeroing on alloc
with __GFP_ZERO can be skipped as well.  The tradeoff is that detecting
corruption from the poisoning is harder to detect.  This feature also
cannot be used with hibernation since pages are not guaranteed to be
zeroed after hibernation.

Credit to Grsecurity/PaX team for inspiring this work

Signed-off-by: Laura Abbott <labbott@fedoraproject.org>
Acked-by: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Mathias Krause <minipli@googlemail.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Jianyu Zhan <nasa4836@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/mm.h
include/linux/poison.h
kernel/power/hibernate.c
mm/Kconfig.debug
mm/page_alloc.c
mm/page_ext.c
mm/page_poison.c

index 99dcc8f36e28f9c7e1c049d22f310bbbad27b156..b97243d6aa498ba5979f66fcc6789d23d41dcfe6 100644 (file)
@@ -2179,10 +2179,12 @@ extern int apply_to_page_range(struct mm_struct *mm, unsigned long address,
 #ifdef CONFIG_PAGE_POISONING
 extern bool page_poisoning_enabled(void);
 extern void kernel_poison_pages(struct page *page, int numpages, int enable);
+extern bool page_is_poisoned(struct page *page);
 #else
 static inline bool page_poisoning_enabled(void) { return false; }
 static inline void kernel_poison_pages(struct page *page, int numpages,
                                        int enable) { }
+static inline bool page_is_poisoned(struct page *page) { return false; }
 #endif
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
index 4a27153574e2839546fb40ee8e8afea483e33cef..51334edec506815a70868dd451d5c72944ed0a59 100644 (file)
 #define TIMER_ENTRY_STATIC     ((void *) 0x300 + POISON_POINTER_DELTA)
 
 /********** mm/debug-pagealloc.c **********/
+#ifdef CONFIG_PAGE_POISONING_ZERO
+#define PAGE_POISON 0x00
+#else
 #define PAGE_POISON 0xaa
+#endif
 
 /********** mm/page_alloc.c ************/
 
index b7342a24f559211934fb90db21a4a06d5479b21a..aa0f26b5842659a5c12b794ed8535e77edd44f15 100644 (file)
@@ -1158,6 +1158,22 @@ static int __init kaslr_nohibernate_setup(char *str)
        return nohibernate_setup(str);
 }
 
+static int __init page_poison_nohibernate_setup(char *str)
+{
+#ifdef CONFIG_PAGE_POISONING_ZERO
+       /*
+        * The zeroing option for page poison skips the checks on alloc.
+        * since hibernation doesn't save free pages there's no way to
+        * guarantee the pages will still be zeroed.
+        */
+       if (!strcmp(str, "on")) {
+               pr_info("Disabling hibernation due to page poisoning\n");
+               return nohibernate_setup(str);
+       }
+#endif
+       return 1;
+}
+
 __setup("noresume", noresume_setup);
 __setup("resume_offset=", resume_offset_setup);
 __setup("resume=", resume_setup);
@@ -1166,3 +1182,4 @@ __setup("resumewait", resumewait_setup);
 __setup("resumedelay=", resumedelay_setup);
 __setup("nohibernate", nohibernate_setup);
 __setup("kaslr", kaslr_nohibernate_setup);
+__setup("page_poison=", page_poison_nohibernate_setup);
index 1f99f9a0deaeeda11ed8435856978ae75885d721..5c50b238b7705a5a4e2ce9a1845e11fc76ec4503 100644 (file)
@@ -65,3 +65,17 @@ config PAGE_POISONING_NO_SANITY
 
           If you are only interested in sanitization, say Y. Otherwise
           say N.
+
+config PAGE_POISONING_ZERO
+       bool "Use zero for poisoning instead of random data"
+       depends on PAGE_POISONING
+       ---help---
+          Instead of using the existing poison value, fill the pages with
+          zeros. This makes it harder to detect when errors are occurring
+          due to sanitization but the zeroing at free means that it is
+          no longer necessary to write zeros when GFP_ZERO is used on
+          allocation.
+
+          Enabling page poisoning with this option will disable hibernation
+
+          If unsure, say N
index 2a08349fbab288222c26681c44eb1e91c6ebca2d..50897dcaefdb9282c5af2b7f704debb7dc47d84a 100644 (file)
@@ -1405,15 +1405,24 @@ static inline int check_new_page(struct page *page)
        return 0;
 }
 
+static inline bool free_pages_prezeroed(bool poisoned)
+{
+       return IS_ENABLED(CONFIG_PAGE_POISONING_ZERO) &&
+               page_poisoning_enabled() && poisoned;
+}
+
 static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
                                                                int alloc_flags)
 {
        int i;
+       bool poisoned = true;
 
        for (i = 0; i < (1 << order); i++) {
                struct page *p = page + i;
                if (unlikely(check_new_page(p)))
                        return 1;
+               if (poisoned)
+                       poisoned &= page_is_poisoned(p);
        }
 
        set_page_private(page, 0);
@@ -1424,7 +1433,7 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,
        kernel_poison_pages(page, 1 << order, 1);
        kasan_alloc_pages(page, order);
 
-       if (gfp_flags & __GFP_ZERO)
+       if (!free_pages_prezeroed(poisoned) && (gfp_flags & __GFP_ZERO))
                for (i = 0; i < (1 << order); i++)
                        clear_highpage(page + i);
 
index 292ca7b8debd2c27c87d056e1ea4872d3094c756..2d864e64f7fe3d4128e4cf043a3d0b93374bcfea 100644 (file)
@@ -106,12 +106,15 @@ struct page_ext *lookup_page_ext(struct page *page)
        struct page_ext *base;
 
        base = NODE_DATA(page_to_nid(page))->node_page_ext;
-#ifdef CONFIG_DEBUG_VM
+#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING)
        /*
         * The sanity checks the page allocator does upon freeing a
         * page can reach here before the page_ext arrays are
         * allocated when feeding a range of pages to the allocator
         * for the first time during bootup or memory hotplug.
+        *
+        * This check is also necessary for ensuring page poisoning
+        * works as expected when enabled
         */
        if (unlikely(!base))
                return NULL;
@@ -180,12 +183,15 @@ struct page_ext *lookup_page_ext(struct page *page)
 {
        unsigned long pfn = page_to_pfn(page);
        struct mem_section *section = __pfn_to_section(pfn);
-#ifdef CONFIG_DEBUG_VM
+#if defined(CONFIG_DEBUG_VM) || defined(CONFIG_PAGE_POISONING)
        /*
         * The sanity checks the page allocator does upon freeing a
         * page can reach here before the page_ext arrays are
         * allocated when feeding a range of pages to the allocator
         * for the first time during bootup or memory hotplug.
+        *
+        * This check is also necessary for ensuring page poisoning
+        * works as expected when enabled
         */
        if (!section->page_ext)
                return NULL;
index 89d3bc77363327a62392b6a34243df01ffde4064..479e7ea2bea6651d00f4b4d363b9bc9ab952591b 100644 (file)
@@ -71,11 +71,14 @@ static inline void clear_page_poison(struct page *page)
        __clear_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags);
 }
 
-static inline bool page_poison(struct page *page)
+bool page_is_poisoned(struct page *page)
 {
        struct page_ext *page_ext;
 
        page_ext = lookup_page_ext(page);
+       if (!page_ext)
+               return false;
+
        return test_bit(PAGE_EXT_DEBUG_POISON, &page_ext->flags);
 }
 
@@ -137,7 +140,7 @@ static void unpoison_page(struct page *page)
 {
        void *addr;
 
-       if (!page_poison(page))
+       if (!page_is_poisoned(page))
                return;
 
        addr = kmap_atomic(page);