linux-mm.kvack.org archive mirror
 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
>>
>
>
>

--
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>

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

Thread overview: 9+ 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 ` [PATCHv2 1/2] mm/page_poison.c: Enable PAGE_POISONING as a separate option Laura Abbott
2016-01-29  2:38 ` [PATCHv2 2/2] mm/page_poisoning.c: Allow for zero poisoning Laura Abbott
2016-01-29  3:55   ` Rafael J. Wysocki
2016-01-29  4:46   ` Kees Cook
2016-01-29 21:32     ` Laura Abbott [this message]
2016-01-29 10:45   ` Pavel Machek
2016-01-29 21:36     ` Laura Abbott
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).