All of lore.kernel.org
 help / color / mirror / Atom feed
From: Laura Abbott <labbott@redhat.com>
To: Kees Cook <keescook@chromium.org>,
	Laura Abbott <labbott@fedoraproject.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
	Vlastimil Babka <vbabka@suse.cz>, Michal Hocko <mhocko@suse.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Pavel Machek <pavel@ucw.cz>, Len Brown <len.brown@intel.com>,
	Linux-MM <linux-mm@kvack.org>,
	LKML <linux-kernel@vger.kernel.org>,
	"kernel-hardening@lists.openwall.com" 
	<kernel-hardening@lists.openwall.com>,
	linux-pm@vger.kernel.org
Subject: Re: [PATCHv2 2/2] mm/page_poisoning.c: Allow for zero poisoning
Date: Fri, 29 Jan 2016 13:32:44 -0800	[thread overview]
Message-ID: <56ABDA7C.5080206@redhat.com> (raw)
In-Reply-To: <CAGXu5jL2MJkJS8H7Gxbtw1P+FREa2XgF_7xkVMgqSvGXRLWAJw@mail.gmail.com>

On 01/28/2016 08:46 PM, Kees Cook wrote:
> On Thu, Jan 28, 2016 at 6:38 PM, Laura Abbott <labbott@fedoraproject.org> wrote:
>> 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>
>> ---
>>   include/linux/mm.h       |  2 ++
>>   include/linux/poison.h   |  4 ++++
>>   kernel/power/hibernate.c | 17 +++++++++++++++++
>>   mm/Kconfig.debug         | 14 ++++++++++++++
>>   mm/page_alloc.c          | 11 ++++++++++-
>>   mm/page_ext.c            | 10 ++++++++--
>>   mm/page_poison.c         |  7 +++++--
>>   7 files changed, 60 insertions(+), 5 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 966bf0e..59ce0dc 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -2177,10 +2177,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
>> diff --git a/include/linux/poison.h b/include/linux/poison.h
>> index 4a27153..51334ed 100644
>> --- a/include/linux/poison.h
>> +++ b/include/linux/poison.h
>> @@ -30,7 +30,11 @@
>>   #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 ************/
>>
>> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
>> index b7342a2..aa0f26b 100644
>> --- a/kernel/power/hibernate.c
>> +++ b/kernel/power/hibernate.c
>> @@ -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);
>> diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
>> index 25c98ae..3d3b954 100644
>> --- a/mm/Kconfig.debug
>> +++ b/mm/Kconfig.debug
>> @@ -48,3 +48,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.
>
> May be worth noting the security benefit in this help text.
>

This is supposed to build on the existing page poisoning which mentions the
security bit. I think this text needs to be clarified how this works.
  
>> +
>> +          Enabling page poisoning with this option will disable hibernation
>
> This isn't obvious to me. It looks like you need to both use
> CONFIG_PAGE_POISOING_ZERO and put "page_poison=on" on the command line
> to enable it?

Yeah, this isn't really clear. I'll make it more obvious this is an extension
of page poisoning so page poisoning must be enabled first.

>
> -Kees
>

Thanks,
Laura
  
>> +
>> +          If unsure, say N
>> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
>> index cc4762a..59bd9dc 100644
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -1382,15 +1382,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);
>> @@ -1401,7 +1410,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);
>>
>> diff --git a/mm/page_ext.c b/mm/page_ext.c
>> index 292ca7b..2d864e6 100644
>> --- a/mm/page_ext.c
>> +++ b/mm/page_ext.c
>> @@ -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;
>> diff --git a/mm/page_poison.c b/mm/page_poison.c
>> index 89d3bc7..479e7ea 100644
>> --- a/mm/page_poison.c
>> +++ b/mm/page_poison.c
>> @@ -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);
>> --
>> 2.5.0
>>
>
>
>

WARNING: multiple messages have this Message-ID (diff)
From: Laura Abbott <labbott@redhat.com>
To: Kees Cook <keescook@chromium.org>,
	Laura Abbott <labbott@fedoraproject.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
	Vlastimil Babka <vbabka@suse.cz>, Michal Hocko <mhocko@suse.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Pavel Machek <pavel@ucw.cz>, Len Brown <len.brown@intel.com>,
	Linux-MM <linux-mm@kvack.org>,
	LKML <linux-kernel@vger.kernel.org>,
	"kernel-hardening@lists.openwall.com"
	<kernel-hardening@lists.openwall.com>,
	linux-pm@vger.kernel.org
Subject: Re: [PATCHv2 2/2] mm/page_poisoning.c: Allow for zero poisoning
Date: Fri, 29 Jan 2016 13:32:44 -0800	[thread overview]
Message-ID: <56ABDA7C.5080206@redhat.com> (raw)
In-Reply-To: <CAGXu5jL2MJkJS8H7Gxbtw1P+FREa2XgF_7xkVMgqSvGXRLWAJw@mail.gmail.com>

On 01/28/2016 08:46 PM, Kees Cook wrote:
> On Thu, Jan 28, 2016 at 6:38 PM, Laura Abbott <labbott@fedoraproject.org> wrote:
>> 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>
>> ---
>>   include/linux/mm.h       |  2 ++
>>   include/linux/poison.h   |  4 ++++
>>   kernel/power/hibernate.c | 17 +++++++++++++++++
>>   mm/Kconfig.debug         | 14 ++++++++++++++
>>   mm/page_alloc.c          | 11 ++++++++++-
>>   mm/page_ext.c            | 10 ++++++++--
>>   mm/page_poison.c         |  7 +++++--
>>   7 files changed, 60 insertions(+), 5 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 966bf0e..59ce0dc 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -2177,10 +2177,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
>> diff --git a/include/linux/poison.h b/include/linux/poison.h
>> index 4a27153..51334ed 100644
>> --- a/include/linux/poison.h
>> +++ b/include/linux/poison.h
>> @@ -30,7 +30,11 @@
>>   #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 ************/
>>
>> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
>> index b7342a2..aa0f26b 100644
>> --- a/kernel/power/hibernate.c
>> +++ b/kernel/power/hibernate.c
>> @@ -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);
>> diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
>> index 25c98ae..3d3b954 100644
>> --- a/mm/Kconfig.debug
>> +++ b/mm/Kconfig.debug
>> @@ -48,3 +48,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.
>
> May be worth noting the security benefit in this help text.
>

This is supposed to build on the existing page poisoning which mentions the
security bit. I think this text needs to be clarified how this works.
  
>> +
>> +          Enabling page poisoning with this option will disable hibernation
>
> This isn't obvious to me. It looks like you need to both use
> CONFIG_PAGE_POISOING_ZERO and put "page_poison=on" on the command line
> to enable it?

Yeah, this isn't really clear. I'll make it more obvious this is an extension
of page poisoning so page poisoning must be enabled first.

>
> -Kees
>

Thanks,
Laura
  
>> +
>> +          If unsure, say N
>> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
>> index cc4762a..59bd9dc 100644
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -1382,15 +1382,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);
>> @@ -1401,7 +1410,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);
>>
>> diff --git a/mm/page_ext.c b/mm/page_ext.c
>> index 292ca7b..2d864e6 100644
>> --- a/mm/page_ext.c
>> +++ b/mm/page_ext.c
>> @@ -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;
>> diff --git a/mm/page_poison.c b/mm/page_poison.c
>> index 89d3bc7..479e7ea 100644
>> --- a/mm/page_poison.c
>> +++ b/mm/page_poison.c
>> @@ -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);
>> --
>> 2.5.0
>>
>
>
>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

WARNING: multiple messages have this Message-ID (diff)
From: Laura Abbott <labbott@redhat.com>
To: Kees Cook <keescook@chromium.org>,
	Laura Abbott <labbott@fedoraproject.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
	Vlastimil Babka <vbabka@suse.cz>, Michal Hocko <mhocko@suse.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Pavel Machek <pavel@ucw.cz>, Len Brown <len.brown@intel.com>,
	Linux-MM <linux-mm@kvack.org>,
	LKML <linux-kernel@vger.kernel.org>,
	"kernel-hardening@lists.openwall.com"
	<kernel-hardening@lists.openwall.com>,
	linux-pm@vger.kernel.org
Subject: [kernel-hardening] Re: [PATCHv2 2/2] mm/page_poisoning.c: Allow for zero poisoning
Date: Fri, 29 Jan 2016 13:32:44 -0800	[thread overview]
Message-ID: <56ABDA7C.5080206@redhat.com> (raw)
In-Reply-To: <CAGXu5jL2MJkJS8H7Gxbtw1P+FREa2XgF_7xkVMgqSvGXRLWAJw@mail.gmail.com>

On 01/28/2016 08:46 PM, Kees Cook wrote:
> On Thu, Jan 28, 2016 at 6:38 PM, Laura Abbott <labbott@fedoraproject.org> wrote:
>> 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>
>> ---
>>   include/linux/mm.h       |  2 ++
>>   include/linux/poison.h   |  4 ++++
>>   kernel/power/hibernate.c | 17 +++++++++++++++++
>>   mm/Kconfig.debug         | 14 ++++++++++++++
>>   mm/page_alloc.c          | 11 ++++++++++-
>>   mm/page_ext.c            | 10 ++++++++--
>>   mm/page_poison.c         |  7 +++++--
>>   7 files changed, 60 insertions(+), 5 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 966bf0e..59ce0dc 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -2177,10 +2177,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
>> diff --git a/include/linux/poison.h b/include/linux/poison.h
>> index 4a27153..51334ed 100644
>> --- a/include/linux/poison.h
>> +++ b/include/linux/poison.h
>> @@ -30,7 +30,11 @@
>>   #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 ************/
>>
>> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
>> index b7342a2..aa0f26b 100644
>> --- a/kernel/power/hibernate.c
>> +++ b/kernel/power/hibernate.c
>> @@ -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);
>> diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
>> index 25c98ae..3d3b954 100644
>> --- a/mm/Kconfig.debug
>> +++ b/mm/Kconfig.debug
>> @@ -48,3 +48,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.
>
> May be worth noting the security benefit in this help text.
>

This is supposed to build on the existing page poisoning which mentions the
security bit. I think this text needs to be clarified how this works.
  
>> +
>> +          Enabling page poisoning with this option will disable hibernation
>
> This isn't obvious to me. It looks like you need to both use
> CONFIG_PAGE_POISOING_ZERO and put "page_poison=on" on the command line
> to enable it?

Yeah, this isn't really clear. I'll make it more obvious this is an extension
of page poisoning so page poisoning must be enabled first.

>
> -Kees
>

Thanks,
Laura
  
>> +
>> +          If unsure, say N
>> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
>> index cc4762a..59bd9dc 100644
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -1382,15 +1382,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);
>> @@ -1401,7 +1410,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);
>>
>> diff --git a/mm/page_ext.c b/mm/page_ext.c
>> index 292ca7b..2d864e6 100644
>> --- a/mm/page_ext.c
>> +++ b/mm/page_ext.c
>> @@ -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;
>> diff --git a/mm/page_poison.c b/mm/page_poison.c
>> index 89d3bc7..479e7ea 100644
>> --- a/mm/page_poison.c
>> +++ b/mm/page_poison.c
>> @@ -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);
>> --
>> 2.5.0
>>
>
>
>

  reply	other threads:[~2016-01-29 21:32 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-29  2:38 [PATCHv2 0/2] Sanitization of buddy pages Laura Abbott
2016-01-29  2:38 ` [kernel-hardening] " Laura Abbott
2016-01-29  2:38 ` Laura Abbott
2016-01-29  2:38 ` [PATCHv2 1/2] mm/page_poison.c: Enable PAGE_POISONING as a separate option Laura Abbott
2016-01-29  2:38   ` [kernel-hardening] " Laura Abbott
2016-01-29  2:38   ` Laura Abbott
2016-01-29  2:38 ` [PATCHv2 2/2] mm/page_poisoning.c: Allow for zero poisoning Laura Abbott
2016-01-29  2:38   ` [kernel-hardening] " Laura Abbott
2016-01-29  2:38   ` Laura Abbott
2016-01-29  3:55   ` Rafael J. Wysocki
2016-01-29  3:55     ` [kernel-hardening] " Rafael J. Wysocki
2016-01-29  3:55     ` Rafael J. Wysocki
2016-01-29  4:46   ` Kees Cook
2016-01-29  4:46     ` [kernel-hardening] " Kees Cook
2016-01-29  4:46     ` Kees Cook
2016-01-29 21:32     ` Laura Abbott [this message]
2016-01-29 21:32       ` [kernel-hardening] " Laura Abbott
2016-01-29 21:32       ` Laura Abbott
2016-01-29 21:32       ` Laura Abbott
2016-01-29 10:45   ` Pavel Machek
2016-01-29 10:45     ` [kernel-hardening] " Pavel Machek
2016-01-29 10:45     ` Pavel Machek
2016-01-29 21:36     ` Laura Abbott
2016-01-29 21:36       ` [kernel-hardening] " Laura Abbott
2016-01-29 21:36       ` Laura Abbott
2016-01-30 15:30       ` Pavel Machek
2016-01-30 15:30         ` [kernel-hardening] " Pavel Machek
2016-01-30 15:30         ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56ABDA7C.5080206@redhat.com \
    --to=labbott@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=keescook@chromium.org \
    --cc=kernel-hardening@lists.openwall.com \
    --cc=kirill.shutemov@linux.intel.com \
    --cc=labbott@fedoraproject.org \
    --cc=len.brown@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=mhocko@suse.com \
    --cc=pavel@ucw.cz \
    --cc=rjw@rjwysocki.net \
    --cc=vbabka@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.