From: Nicholas Piggin <npiggin@gmail.com> Date: 2018-05-30 10:31:33
The stores to update the SLB shadow area must be made as they appear
in the C code, so that the hypervisor does not see an entry with
mismatched vsid and esid. Use WRITE_ONCE for this.
GCC has been observed to elide the first store to esid in the update,
which means that if the hypervisor interrupts the guest after storing
to vsid, it could see an entry with old esid and new vsid, which may
possibly result in memory corruption.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/slb.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2018-05-31 14:22:22
Nicholas Piggin [off-list ref] writes:
quoted hunk
The stores to update the SLB shadow area must be made as they appear
in the C code, so that the hypervisor does not see an entry with
mismatched vsid and esid. Use WRITE_ONCE for this.
GCC has been observed to elide the first store to esid in the update,
which means that if the hypervisor interrupts the guest after storing
to vsid, it could see an entry with old esid and new vsid, which may
possibly result in memory corruption.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/slb.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Nicholas Piggin <npiggin@gmail.com> Date: 2018-05-31 22:52:41
On Fri, 01 Jun 2018 00:22:21 +1000
Michael Ellerman [off-list ref] wrote:
Nicholas Piggin [off-list ref] writes:
quoted
The stores to update the SLB shadow area must be made as they appear
in the C code, so that the hypervisor does not see an entry with
mismatched vsid and esid. Use WRITE_ONCE for this.
GCC has been observed to elide the first store to esid in the update,
which means that if the hypervisor interrupts the guest after storing
to vsid, it could see an entry with old esid and new vsid, which may
possibly result in memory corruption.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/slb.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
@@ -63,14 +63,14 @@ static inline void slb_shadow_update(unsigned long ea, int ssize,*updatingit.Nowritebarriersareneededhere,provided*weonlyupdatethecurrentCPU'sSLBshadowbuffer.*/-p->save_area[index].esid=0;-p->save_area[index].vsid=cpu_to_be64(mk_vsid_data(ea,ssize,flags));-p->save_area[index].esid=cpu_to_be64(mk_esid_data(ea,ssize,index));+WRITE_ONCE(p->save_area[index].esid,0);+WRITE_ONCE(p->save_area[index].vsid,cpu_to_be64(mk_vsid_data(ea,ssize,flags)));+WRITE_ONCE(p->save_area[index].esid,cpu_to_be64(mk_esid_data(ea,ssize,index)));
What's the code-gen for that look like? I suspect it's terrible?
Yeah it's not great.
Should we just do it in inline-asm I wonder?
There should be no fundamental correctness reason why we can't store
to a volatile with a byteswap store. The other option we could do is
add a compiler barrier() between each store. The reason I didn't is
that in theory we don't need to invalidate all memory contents here,
but in practice probably the end result code generation would be
better.
Thanks,
Nick
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2018-06-01 11:13:46
Nicholas Piggin [off-list ref] writes:
On Fri, 01 Jun 2018 00:22:21 +1000
Michael Ellerman [off-list ref] wrote:
quoted
Nicholas Piggin [off-list ref] writes:
quoted
The stores to update the SLB shadow area must be made as they appear
in the C code, so that the hypervisor does not see an entry with
mismatched vsid and esid. Use WRITE_ONCE for this.
GCC has been observed to elide the first store to esid in the update,
which means that if the hypervisor interrupts the guest after storing
to vsid, it could see an entry with old esid and new vsid, which may
possibly result in memory corruption.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/mm/slb.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
@@ -63,14 +63,14 @@ static inline void slb_shadow_update(unsigned long ea, int ssize,*updatingit.Nowritebarriersareneededhere,provided*weonlyupdatethecurrentCPU'sSLBshadowbuffer.*/-p->save_area[index].esid=0;-p->save_area[index].vsid=cpu_to_be64(mk_vsid_data(ea,ssize,flags));-p->save_area[index].esid=cpu_to_be64(mk_esid_data(ea,ssize,index));+WRITE_ONCE(p->save_area[index].esid,0);+WRITE_ONCE(p->save_area[index].vsid,cpu_to_be64(mk_vsid_data(ea,ssize,flags)));+WRITE_ONCE(p->save_area[index].esid,cpu_to_be64(mk_esid_data(ea,ssize,index)));
What's the code-gen for that look like? I suspect it's terrible?
Yeah it's not great.
Actually with GCC 7 the WRITE_ONCE() doesn't make it any worse.
Which is a little suspicious. But it is doing the first store:
li r10,0 # r10 = 0
ld r29,56(r13) # r29 = paca->slb_shadow_ptr
rldicr r8,r31,4,59 # r8 = index
rldicr r9,r9,32,31
add r29,r29,r8 # r29 = r29 + index
oris r9,r9,65535
std r10,16(r29) # esid = r10 = 0
So I'll just merge this as-is.
cheers
What's the code-gen for that look like? I suspect it's terrible?
Yeah it's not great.
quoted
Should we just do it in inline-asm I wonder?
That is my recommendation: that will work for all compiler versions.
There should be no fundamental correctness reason why we can't store
to a volatile with a byteswap store.
There are may operations that are *not* correct to merge into a volatile
memory access, and which are fine is different for every arch. GCC
simply disallows combining anything into any volatile memory by default.
This is kind of fine because volatile already means "I want this to go
slow", in common cases ;-)
I'll see what I can do to make the byteswap load/stores work with volatile
(for powerpc).
The other option we could do is
add a compiler barrier() between each store. The reason I didn't is
that in theory we don't need to invalidate all memory contents here,
but in practice probably the end result code generation would be
better.
Something like
p->save_area[index].esid = 0;
asm("" : : "m"(p->save_area[index].esid));
p->save_area[index].vsid = cpu_to_be64(mk_vsid_data(ea, ssize, flags));
asm("" : : "m"(p->save_area[index].vsid));
p->save_area[index].esid = cpu_to_be64(mk_esid_data(ea, ssize, index));
should do the trick (and once again for the second write to esid, if you
want to be sure it is not optimised away).
Segher
From: Michael Ellerman <hidden> Date: 2018-06-04 14:11:26
On Wed, 2018-05-30 at 10:31:22 UTC, Nicholas Piggin wrote:
The stores to update the SLB shadow area must be made as they appear
in the C code, so that the hypervisor does not see an entry with
mismatched vsid and esid. Use WRITE_ONCE for this.
GCC has been observed to elide the first store to esid in the update,
which means that if the hypervisor interrupts the guest after storing
to vsid, it could see an entry with old esid and new vsid, which may
possibly result in memory corruption.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>