From: Nicholas Piggin <npiggin@gmail.com> Date: 2019-06-07 03:59:59
Commit 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion
in pte helpers") changed the actual bitwise tests in pte_access_permitted
by using pte_write() and pte_present() helpers rather than raw bitwise
testing _PAGE_WRITE and _PAGE_PRESENT bits.
The pte_present change now returns true for ptes which are !_PAGE_PRESENT
and _PAGE_INVALID, which is the combination used by pmdp_invalidate to
synchronize access from lock-free lookups. pte_access_permitted is used by
pmd_access_permitted, so allowing GUP lock free access to proceed with
such PTEs breaks this synchronisation.
This bug has been observed on HPT host, with random crashes and corruption
in guests, usually together with bad PMD messages in the host.
Fix this by adding an explicit check in pmd_access_permitted, and
documenting the condition explicitly.
The pte_write() change should be okay, and would prevent GUP from falling
back to the slow path when encountering savedwrite ptes, which matches
what x86 (that does not implement savedwrite) does.
Fixes: 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion in pte helpers")
Cc: Aneesh Kumar K.V <redacted>
Cc: Christophe Leroy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
I accounted for Aneesh's and Christophe's feedback, except I couldn't
find a good way to replace the ifdef with IS_ENABLED because of
_PAGE_INVALID etc., but at least cleaned that up a bit nicer.
Patch 1 solves a problem I can hit quite reliably running HPT/HPT KVM.
Patch 2 was noticed by Aneesh when inspecting code for similar bugs.
They should probably both be merged in stable kernels after upstream.
arch/powerpc/include/asm/book3s/64/pgtable.h | 30 ++++++++++++++++++++
arch/powerpc/mm/book3s64/pgtable.c | 3 ++
2 files changed, 33 insertions(+)
From: Nicholas Piggin <npiggin@gmail.com> Date: 2019-06-07 04:02:09
The change to pmdp_invalidate to mark the pmd with _PAGE_INVALID broke
the synchronisation against lock free lookups, __find_linux_pte's
pmd_none check no longer returns true for such cases.
Fix this by adding a check for this condition as well.
Fixes: da7ad366b497 ("powerpc/mm/book3s: Update pmd_present to look at _PAGE_PRESENT bit")
Cc: Christophe Leroy <redacted>
Suggested-by: Aneesh Kumar K.V <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/pgtable.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
Commit 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion
in pte helpers") changed the actual bitwise tests in pte_access_permitted
by using pte_write() and pte_present() helpers rather than raw bitwise
testing _PAGE_WRITE and _PAGE_PRESENT bits.
The pte_present change now returns true for ptes which are !_PAGE_PRESENT
and _PAGE_INVALID, which is the combination used by pmdp_invalidate to
synchronize access from lock-free lookups. pte_access_permitted is used by
pmd_access_permitted, so allowing GUP lock free access to proceed with
such PTEs breaks this synchronisation.
This bug has been observed on HPT host, with random crashes and corruption
in guests, usually together with bad PMD messages in the host.
Fix this by adding an explicit check in pmd_access_permitted, and
documenting the condition explicitly.
The pte_write() change should be okay, and would prevent GUP from falling
back to the slow path when encountering savedwrite ptes, which matches
what x86 (that does not implement savedwrite) does.
Fixes: 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion in pte helpers")
Cc: Aneesh Kumar K.V <redacted>
Cc: Christophe Leroy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
I accounted for Aneesh's and Christophe's feedback, except I couldn't
find a good way to replace the ifdef with IS_ENABLED because of
_PAGE_INVALID etc., but at least cleaned that up a bit nicer.
I guess the standard way is to add a pmd_is_serializing() which return
always false in book3s/32/pgtable.h and in nohash/pgtable.h
quoted hunk
Patch 1 solves a problem I can hit quite reliably running HPT/HPT KVM.
Patch 2 was noticed by Aneesh when inspecting code for similar bugs.
They should probably both be merged in stable kernels after upstream.
arch/powerpc/include/asm/book3s/64/pgtable.h | 30 ++++++++++++++++++++
arch/powerpc/mm/book3s64/pgtable.c | 3 ++
2 files changed, 33 insertions(+)
@@ -876,6 +876,23 @@ static inline int pmd_present(pmd_t pmd)returnfalse;}+staticinlineintpmd_is_serializing(pmd_tpmd)
should be static inline bool instead of int ?
Christophe
quoted hunk
+{
+ /*
+ * If the pmd is undergoing a split, the _PAGE_PRESENT bit is clear
+ * and _PAGE_INVALID is set (see pmd_present, pmdp_invalidate).
+ *
+ * This condition may also occur when flushing a pmd while flushing
+ * it (see ptep_modify_prot_start), so callers must ensure this
+ * case is fine as well.
+ */
+ if ((pmd_raw(pmd) & cpu_to_be64(_PAGE_PRESENT | _PAGE_INVALID)) ==
+ cpu_to_be64(_PAGE_INVALID))
+ return true;
+
+ return false;
+}
+
static inline int pmd_bad(pmd_t pmd)
{
if (radix_enabled())
@@ -1092,6 +1109,19 @@ static inline int pmd_protnone(pmd_t pmd) #define pmd_access_permitted pmd_access_permitted static inline bool pmd_access_permitted(pmd_t pmd, bool write) {+ /*+ * pmdp_invalidate sets this combination (which is not caught by+ * !pte_present() check in pte_access_permitted), to prevent+ * lock-free lookups, as part of the serialize_against_pte_lookup()+ * synchronisation.+ *+ * This also catches the case where the PTE's hardware PRESENT bit is+ * cleared while TLB is flushed, which is suboptimal but should not+ * be frequent.+ */+ if (pmd_is_serializing(pmd))+ return false;+ return pte_access_permitted(pmd_pte(pmd), write); }
Commit 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion
in pte helpers") changed the actual bitwise tests in pte_access_permitted
by using pte_write() and pte_present() helpers rather than raw bitwise
testing _PAGE_WRITE and _PAGE_PRESENT bits.
The pte_present change now returns true for ptes which are !_PAGE_PRESENT
and _PAGE_INVALID, which is the combination used by pmdp_invalidate to
synchronize access from lock-free lookups. pte_access_permitted is used by
pmd_access_permitted, so allowing GUP lock free access to proceed with
such PTEs breaks this synchronisation.
This bug has been observed on HPT host, with random crashes and corruption
in guests, usually together with bad PMD messages in the host.
Fix this by adding an explicit check in pmd_access_permitted, and
documenting the condition explicitly.
The pte_write() change should be okay, and would prevent GUP from falling
back to the slow path when encountering savedwrite ptes, which matches
what x86 (that does not implement savedwrite) does.
Reviewed-by: Aneesh Kumar K.V <redacted>
quoted hunk
Fixes: 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion in pte helpers")
Cc: Aneesh Kumar K.V <redacted>
Cc: Christophe Leroy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
I accounted for Aneesh's and Christophe's feedback, except I couldn't
find a good way to replace the ifdef with IS_ENABLED because of
_PAGE_INVALID etc., but at least cleaned that up a bit nicer.
Patch 1 solves a problem I can hit quite reliably running HPT/HPT KVM.
Patch 2 was noticed by Aneesh when inspecting code for similar bugs.
They should probably both be merged in stable kernels after upstream.
arch/powerpc/include/asm/book3s/64/pgtable.h | 30 ++++++++++++++++++++
arch/powerpc/mm/book3s64/pgtable.c | 3 ++
2 files changed, 33 insertions(+)
The change to pmdp_invalidate to mark the pmd with _PAGE_INVALID broke
the synchronisation against lock free lookups, __find_linux_pte's
pmd_none check no longer returns true for such cases.
Fix this by adding a check for this condition as well.
Fixes: da7ad366b497 ("powerpc/mm/book3s: Update pmd_present to look at _PAGE_PRESENT bit")
Cc: Christophe Leroy <redacted>
Suggested-by: Aneesh Kumar K.V <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/pgtable.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
Could get rid of that #ifdef by adding the following in book3s32 and
nohash pgtable.h:
static inline bool pmd_is_serializing() { return false; }
Christophe
if (pmd_trans_huge(pmd) || pmd_devmap(pmd)) {
if (is_thp)
*is_thp = true;
The change to pmdp_invalidate to mark the pmd with _PAGE_INVALID broke
the synchronisation against lock free lookups, __find_linux_pte's
pmd_none check no longer returns true for such cases.
Fix this by adding a check for this condition as well.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2019-06-07 06:36:10
Christophe Leroy's on June 7, 2019 3:35 pm:
Le 07/06/2019 à 05:56, Nicholas Piggin a écrit :
quoted
The change to pmdp_invalidate to mark the pmd with _PAGE_INVALID broke
the synchronisation against lock free lookups, __find_linux_pte's
pmd_none check no longer returns true for such cases.
Fix this by adding a check for this condition as well.
Fixes: da7ad366b497 ("powerpc/mm/book3s: Update pmd_present to look at _PAGE_PRESENT bit")
Cc: Christophe Leroy <redacted>
Suggested-by: Aneesh Kumar K.V <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/pgtable.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
Could get rid of that #ifdef by adding the following in book3s32 and
nohash pgtable.h:
static inline bool pmd_is_serializing() { return false; }
I don't mind either way. If it's an isolated case like this, sometimes
I'm against polluting the sub arch code with it.
It's up to you I can change that if you prefer.
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2019-06-07 06:43:40
Christophe Leroy's on June 7, 2019 3:34 pm:
Le 07/06/2019 à 05:56, Nicholas Piggin a écrit :
quoted
Commit 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion
in pte helpers") changed the actual bitwise tests in pte_access_permitted
by using pte_write() and pte_present() helpers rather than raw bitwise
testing _PAGE_WRITE and _PAGE_PRESENT bits.
The pte_present change now returns true for ptes which are !_PAGE_PRESENT
and _PAGE_INVALID, which is the combination used by pmdp_invalidate to
synchronize access from lock-free lookups. pte_access_permitted is used by
pmd_access_permitted, so allowing GUP lock free access to proceed with
such PTEs breaks this synchronisation.
This bug has been observed on HPT host, with random crashes and corruption
in guests, usually together with bad PMD messages in the host.
Fix this by adding an explicit check in pmd_access_permitted, and
documenting the condition explicitly.
The pte_write() change should be okay, and would prevent GUP from falling
back to the slow path when encountering savedwrite ptes, which matches
what x86 (that does not implement savedwrite) does.
Fixes: 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion in pte helpers")
Cc: Aneesh Kumar K.V <redacted>
Cc: Christophe Leroy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
I accounted for Aneesh's and Christophe's feedback, except I couldn't
find a good way to replace the ifdef with IS_ENABLED because of
_PAGE_INVALID etc., but at least cleaned that up a bit nicer.
I guess the standard way is to add a pmd_is_serializing() which return
always false in book3s/32/pgtable.h and in nohash/pgtable.h
quoted
Patch 1 solves a problem I can hit quite reliably running HPT/HPT KVM.
Patch 2 was noticed by Aneesh when inspecting code for similar bugs.
They should probably both be merged in stable kernels after upstream.
arch/powerpc/include/asm/book3s/64/pgtable.h | 30 ++++++++++++++++++++
arch/powerpc/mm/book3s64/pgtable.c | 3 ++
2 files changed, 33 insertions(+)
@@ -876,6 +876,23 @@ static inline int pmd_present(pmd_t pmd)returnfalse;}+staticinlineintpmd_is_serializing(pmd_tpmd)
should be static inline bool instead of int ?
I think just about all the p?d_blah boolean functions in the tree are
int at the moment, so I followed that pattern.
May be a good tree wide change to make at some point.
Thanks,
Nick
From: Michael Ellerman <hidden> Date: 2019-06-12 05:04:37
On Fri, 2019-06-07 at 03:56:35 UTC, Nicholas Piggin wrote:
Commit 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion
in pte helpers") changed the actual bitwise tests in pte_access_permitted
by using pte_write() and pte_present() helpers rather than raw bitwise
testing _PAGE_WRITE and _PAGE_PRESENT bits.
The pte_present change now returns true for ptes which are !_PAGE_PRESENT
and _PAGE_INVALID, which is the combination used by pmdp_invalidate to
synchronize access from lock-free lookups. pte_access_permitted is used by
pmd_access_permitted, so allowing GUP lock free access to proceed with
such PTEs breaks this synchronisation.
This bug has been observed on HPT host, with random crashes and corruption
in guests, usually together with bad PMD messages in the host.
Fix this by adding an explicit check in pmd_access_permitted, and
documenting the condition explicitly.
The pte_write() change should be okay, and would prevent GUP from falling
back to the slow path when encountering savedwrite ptes, which matches
what x86 (that does not implement savedwrite) does.
Fixes: 1b2443a547f9 ("powerpc/book3s64: Avoid multiple endian conversion in pte helpers")
Cc: Aneesh Kumar K.V <redacted>
Cc: Christophe Leroy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Aneesh Kumar K.V <redacted>
From: Michael Ellerman <hidden> Date: 2019-06-12 05:06:23
On Fri, 2019-06-07 at 03:56:36 UTC, Nicholas Piggin wrote:
The change to pmdp_invalidate to mark the pmd with _PAGE_INVALID broke
the synchronisation against lock free lookups, __find_linux_pte's
pmd_none check no longer returns true for such cases.
Fix this by adding a check for this condition as well.
Fixes: da7ad366b497 ("powerpc/mm/book3s: Update pmd_present to look at _PAGE_PRESENT bit")
Cc: Christophe Leroy <redacted>
Suggested-by: Aneesh Kumar K.V <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Aneesh Kumar K.V <redacted>