]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Mar 2014 23:59:22 +0000 (16:59 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Mar 2014 23:59:22 +0000 (16:59 -0700)
added patches:
mm-close-pagetail-race.patch
netfilter-nf_conntrack_dccp-fix-skb_header_pointer-api-usages.patch

queue-3.10/mm-close-pagetail-race.patch [new file with mode: 0644]
queue-3.10/netfilter-nf_conntrack_dccp-fix-skb_header_pointer-api-usages.patch [new file with mode: 0644]
queue-3.10/series

diff --git a/queue-3.10/mm-close-pagetail-race.patch b/queue-3.10/mm-close-pagetail-race.patch
new file mode 100644 (file)
index 0000000..9df96c6
--- /dev/null
@@ -0,0 +1,232 @@
+From 668f9abbd4334e6c29fa8acd71635c4f9101caa7 Mon Sep 17 00:00:00 2001
+From: David Rientjes <rientjes@google.com>
+Date: Mon, 3 Mar 2014 15:38:18 -0800
+Subject: mm: close PageTail race
+
+From: David Rientjes <rientjes@google.com>
+
+commit 668f9abbd4334e6c29fa8acd71635c4f9101caa7 upstream.
+
+Commit bf6bddf1924e ("mm: introduce compaction and migration for
+ballooned pages") introduces page_count(page) into memory compaction
+which dereferences page->first_page if PageTail(page).
+
+This results in a very rare NULL pointer dereference on the
+aforementioned page_count(page).  Indeed, anything that does
+compound_head(), including page_count() is susceptible to racing with
+prep_compound_page() and seeing a NULL or dangling page->first_page
+pointer.
+
+This patch uses Andrea's implementation of compound_trans_head() that
+deals with such a race and makes it the default compound_head()
+implementation.  This includes a read memory barrier that ensures that
+if PageTail(head) is true that we return a head page that is neither
+NULL nor dangling.  The patch then adds a store memory barrier to
+prep_compound_page() to ensure page->first_page is set.
+
+This is the safest way to ensure we see the head page that we are
+expecting, PageTail(page) is already in the unlikely() path and the
+memory barriers are unfortunately required.
+
+Hugetlbfs is the exception, we don't enforce a store memory barrier
+during init since no race is possible.
+
+Signed-off-by: David Rientjes <rientjes@google.com>
+Cc: Holger Kiehl <Holger.Kiehl@dwd.de>
+Cc: Christoph Lameter <cl@linux.com>
+Cc: Rafael Aquini <aquini@redhat.com>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Michal Hocko <mhocko@suse.cz>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Andrea Arcangeli <aarcange@redhat.com>
+Cc: Rik van Riel <riel@redhat.com>
+Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+
+---
+ drivers/block/aoe/aoecmd.c      |    2 +-
+ drivers/vfio/vfio_iommu_type1.c |    4 ++--
+ fs/proc/page.c                  |    2 +-
+ include/linux/huge_mm.h         |   18 ------------------
+ include/linux/mm.h              |   14 ++++++++++++--
+ mm/ksm.c                        |    2 +-
+ mm/memory-failure.c             |    2 +-
+ mm/page_alloc.c                 |    4 +++-
+ mm/swap.c                       |    4 ++--
+ virt/kvm/kvm_main.c             |    4 ++--
+ 10 files changed, 25 insertions(+), 31 deletions(-)
+
+--- a/drivers/block/aoe/aoecmd.c
++++ b/drivers/block/aoe/aoecmd.c
+@@ -899,7 +899,7 @@ bio_pageinc(struct bio *bio)
+                * but this has never been seen here.
+                */
+               if (unlikely(PageCompound(page)))
+-                      if (compound_trans_head(page) != page) {
++                      if (compound_head(page) != page) {
+                               pr_crit("page tail used for block I/O\n");
+                               BUG();
+                       }
+--- a/drivers/vfio/vfio_iommu_type1.c
++++ b/drivers/vfio/vfio_iommu_type1.c
+@@ -138,12 +138,12 @@ static bool is_invalid_reserved_pfn(unsi
+       if (pfn_valid(pfn)) {
+               bool reserved;
+               struct page *tail = pfn_to_page(pfn);
+-              struct page *head = compound_trans_head(tail);
++              struct page *head = compound_head(tail);
+               reserved = !!(PageReserved(head));
+               if (head != tail) {
+                       /*
+                        * "head" is not a dangling pointer
+-                       * (compound_trans_head takes care of that)
++                       * (compound_head takes care of that)
+                        * but the hugepage may have been split
+                        * from under us (and we may not hold a
+                        * reference count on the head page so it can
+--- a/fs/proc/page.c
++++ b/fs/proc/page.c
+@@ -121,7 +121,7 @@ u64 stable_page_flags(struct page *page)
+        * just checks PG_head/PG_tail, so we need to check PageLRU to make
+        * sure a given page is a thp, not a non-huge compound page.
+        */
+-      else if (PageTransCompound(page) && PageLRU(compound_trans_head(page)))
++      else if (PageTransCompound(page) && PageLRU(compound_head(page)))
+               u |= 1 << KPF_THP;
+       /*
+--- a/include/linux/huge_mm.h
++++ b/include/linux/huge_mm.h
+@@ -159,23 +159,6 @@ static inline int hpage_nr_pages(struct
+               return HPAGE_PMD_NR;
+       return 1;
+ }
+-static inline struct page *compound_trans_head(struct page *page)
+-{
+-      if (PageTail(page)) {
+-              struct page *head;
+-              head = page->first_page;
+-              smp_rmb();
+-              /*
+-               * head may be a dangling pointer.
+-               * __split_huge_page_refcount clears PageTail before
+-               * overwriting first_page, so if PageTail is still
+-               * there it means the head pointer isn't dangling.
+-               */
+-              if (PageTail(page))
+-                      return head;
+-      }
+-      return page;
+-}
+ extern int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
+                               unsigned long addr, pmd_t pmd, pmd_t *pmdp);
+@@ -205,7 +188,6 @@ static inline int split_huge_page(struct
+       do { } while (0)
+ #define split_huge_page_pmd_mm(__mm, __address, __pmd)        \
+       do { } while (0)
+-#define compound_trans_head(page) compound_head(page)
+ static inline int hugepage_madvise(struct vm_area_struct *vma,
+                                  unsigned long *vm_flags, int advice)
+ {
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -361,8 +361,18 @@ static inline void compound_unlock_irqre
+ static inline struct page *compound_head(struct page *page)
+ {
+-      if (unlikely(PageTail(page)))
+-              return page->first_page;
++      if (unlikely(PageTail(page))) {
++              struct page *head = page->first_page;
++
++              /*
++               * page->first_page may be a dangling pointer to an old
++               * compound page, so recheck that it is still a tail
++               * page before returning.
++               */
++              smp_rmb();
++              if (likely(PageTail(page)))
++                      return head;
++      }
+       return page;
+ }
+--- a/mm/ksm.c
++++ b/mm/ksm.c
+@@ -444,7 +444,7 @@ static void break_cow(struct rmap_item *
+ static struct page *page_trans_compound_anon(struct page *page)
+ {
+       if (PageTransCompound(page)) {
+-              struct page *head = compound_trans_head(page);
++              struct page *head = compound_head(page);
+               /*
+                * head may actually be splitted and freed from under
+                * us but it's ok here.
+--- a/mm/memory-failure.c
++++ b/mm/memory-failure.c
+@@ -1544,7 +1544,7 @@ int soft_offline_page(struct page *page,
+ {
+       int ret;
+       unsigned long pfn = page_to_pfn(page);
+-      struct page *hpage = compound_trans_head(page);
++      struct page *hpage = compound_head(page);
+       if (PageHWPoison(page)) {
+               pr_info("soft offline: %#lx page already poisoned\n", pfn);
+--- a/mm/page_alloc.c
++++ b/mm/page_alloc.c
+@@ -360,9 +360,11 @@ void prep_compound_page(struct page *pag
+       __SetPageHead(page);
+       for (i = 1; i < nr_pages; i++) {
+               struct page *p = page + i;
+-              __SetPageTail(p);
+               set_page_count(p, 0);
+               p->first_page = page;
++              /* Make sure p->first_page is always valid for PageTail() */
++              smp_wmb();
++              __SetPageTail(p);
+       }
+ }
+--- a/mm/swap.c
++++ b/mm/swap.c
+@@ -81,7 +81,7 @@ static void put_compound_page(struct pag
+ {
+       if (unlikely(PageTail(page))) {
+               /* __split_huge_page_refcount can run under us */
+-              struct page *page_head = compound_trans_head(page);
++              struct page *page_head = compound_head(page);
+               if (likely(page != page_head &&
+                          get_page_unless_zero(page_head))) {
+@@ -219,7 +219,7 @@ bool __get_page_tail(struct page *page)
+        */
+       unsigned long flags;
+       bool got = false;
+-      struct page *page_head = compound_trans_head(page);
++      struct page *page_head = compound_head(page);
+       if (likely(page != page_head && get_page_unless_zero(page_head))) {
+               /* Ref to put_compound_page() comment. */
+--- a/virt/kvm/kvm_main.c
++++ b/virt/kvm/kvm_main.c
+@@ -105,12 +105,12 @@ bool kvm_is_mmio_pfn(pfn_t pfn)
+       if (pfn_valid(pfn)) {
+               int reserved;
+               struct page *tail = pfn_to_page(pfn);
+-              struct page *head = compound_trans_head(tail);
++              struct page *head = compound_head(tail);
+               reserved = PageReserved(head);
+               if (head != tail) {
+                       /*
+                        * "head" is not a dangling pointer
+-                       * (compound_trans_head takes care of that)
++                       * (compound_head takes care of that)
+                        * but the hugepage may have been splitted
+                        * from under us (and we may not hold a
+                        * reference count on the head page so it can
diff --git a/queue-3.10/netfilter-nf_conntrack_dccp-fix-skb_header_pointer-api-usages.patch b/queue-3.10/netfilter-nf_conntrack_dccp-fix-skb_header_pointer-api-usages.patch
new file mode 100644 (file)
index 0000000..7a39612
--- /dev/null
@@ -0,0 +1,62 @@
+From b22f5126a24b3b2f15448c3f2a254fc10cbc2b92 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <dborkman@redhat.com>
+Date: Mon, 6 Jan 2014 00:57:54 +0100
+Subject: netfilter: nf_conntrack_dccp: fix skb_header_pointer API usages
+
+From: Daniel Borkmann <dborkman@redhat.com>
+
+commit b22f5126a24b3b2f15448c3f2a254fc10cbc2b92 upstream.
+
+Some occurences in the netfilter tree use skb_header_pointer() in
+the following way ...
+
+  struct dccp_hdr _dh, *dh;
+  ...
+  skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
+
+... where dh itself is a pointer that is being passed as the copy
+buffer. Instead, we need to use &_dh as the forth argument so that
+we're copying the data into an actual buffer that sits on the stack.
+
+Currently, we probably could overwrite memory on the stack (e.g.
+with a possibly mal-formed DCCP packet), but unintentionally, as
+we only want the buffer to be placed into _dh variable.
+
+Fixes: 2bc780499aa3 ("[NETFILTER]: nf_conntrack: add DCCP protocol support")
+Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/netfilter/nf_conntrack_proto_dccp.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/net/netfilter/nf_conntrack_proto_dccp.c
++++ b/net/netfilter/nf_conntrack_proto_dccp.c
+@@ -428,7 +428,7 @@ static bool dccp_new(struct nf_conn *ct,
+       const char *msg;
+       u_int8_t state;
+-      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+       BUG_ON(dh == NULL);
+       state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
+@@ -486,7 +486,7 @@ static int dccp_packet(struct nf_conn *c
+       u_int8_t type, old_state, new_state;
+       enum ct_dccp_roles role;
+-      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+       BUG_ON(dh == NULL);
+       type = dh->dccph_type;
+@@ -577,7 +577,7 @@ static int dccp_error(struct net *net, s
+       unsigned int cscov;
+       const char *msg;
+-      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++      dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+       if (dh == NULL) {
+               msg = "nf_ct_dccp: short packet ";
+               goto out_invalid;
index b88d8fd32879cc3793345cb824d233d379efa7bc..24385fe72f726daf32f3a0bdc3ad4dcf899cfc14 100644 (file)
@@ -5,3 +5,5 @@ input-synaptics-add-manual-min-max-quirk-for-thinkpad-x240.patch
 input-cypress_ps2-don-t-report-as-a-button-pads.patch
 x86-fix-boot-on-uniprocessor-systems.patch
 net-mvneta-rename-mvneta_gmac2_psc_enable-to-mvneta_gmac2_pcs_enable.patch
+mm-close-pagetail-race.patch
+netfilter-nf_conntrack_dccp-fix-skb_header_pointer-api-usages.patch