* Testing the lws_compare_and_swap_2 syscall
@ 2017-10-24 20:03 Christoph Biedl
2017-10-25 1:48 ` John David Anglin
0 siblings, 1 reply; 8+ messages in thread
From: Christoph Biedl @ 2017-10-24 20:03 UTC (permalink / raw)
To: linux-parisc
Hello,
looking into John's recent fix for lws_compare_and_swap_2 on 32bit
systems I got the feeling things still aren't right yet. To defeat
or prove that, also since I'd like to learn more about this ... I wrote
a small program the uses that syscall, and things break galore.
Could you please check the code below[1] for obvious usage errors? Note
the entire cmpxchg2 function was copied from gcc, and the disassembly
output provided by objdump looks correct as far as I can tell.
The program takes four numerical parameters that correspond to the
four parameters of the syscall.
To start with, using the invalid value 4 as size parameter does not
return ENOSYS as I'd expect but crashes my system[2], using both 32 and
64 bit kernel, no root privileges required. This should never happen.
Regards,
Christoph
[1]
======================================================================
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* borrowed from __kernel_cmpxchg2 in libgcc/config/pa/linux-atomic.c in the gcc sources */
static inline long
cmpxchg2 (void *mem, const void *oldval, const void *newval,
int val_size)
{
register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
register unsigned long lws_old asm("r25") = (unsigned long) oldval;
register unsigned long lws_new asm("r24") = (unsigned long) newval;
register int lws_size asm("r23") = val_size;
register long lws_ret asm("r28");
register long lws_errno asm("r21");
asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t"
"ldi %6, %%r20 \n\t"
: "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem),
"+r" (lws_old), "+r" (lws_new), "+r" (lws_size)
: "i" (2)
: "r1", "r20", "r22", "r29", "r31", "fr4", "memory"
);
/* If the kernel LWS call is successful, lws_ret contains 0. */
if (__builtin_expect (lws_ret == 0, 1))
return 0;
if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
__builtin_trap ();
/* If the kernel LWS call fails with no error, return -EBUSY */
if (__builtin_expect (!lws_errno, 0))
return -EBUSY;
return lws_errno;
}
int main (int argc, char **argv) {
if (argc != 5) {
printf ("usage <mem> <old> <new> <size>\n");
exit (1);
}
uint64_t a = atoi (argv[1]);
uint64_t b = atoi (argv[2]);
uint64_t c = atoi (argv[3]);
unsigned long size = atoi (argv[4]);
printf ("a = 0x%016llx, b = 0x%016llx, c = 0x%016llx\n",
a,
b,
c
);
unsigned long r = cmpxchg2 (&a, &b, &c, size);
printf ("a = 0x%016llx\n", a);
printf ("r = 0x%lx\n", r);
return 0;
}
======================================================================
[2]
| Backtrace:
|
| Kernel Fault: Code=26 (Data memory access rights trap) regs=000000007ca8f738 (Addr=0000000000000002)
| CPU: 0 PID: 1289 Comm: a.out Not tainted 4.12.0-2-parisc64-smp #1 Debian 4.12.13-1
| task: 000000007ca8eec0 task.stack: 000000007cb68000
|
| YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
| PSW: 00001000000001101111111100001111 Not tainted
| r00-03 000000ff0806ff0f 0000000040cf2000 0000000000010987 00000000f93963c0
| r04-07 00000000f853fc70 00000000ffffffff 00000000ffffffff 00000000ffffffff
| r08-11 00000000fffffffe 00000000ffffffff 00000000fffffffd 00000000000ed000
| r12-15 00000000ffffffff 0000000000911d28 0000000000000000 0000000000117be8
| r16-19 00000000009ce448 0000000000000000 0000000000000001 00000000f9396328
| r20-23 0000000000000002 00000000000004d4 00000000f844a08c 0000000000000004
| r24-27 00000000f9396330 00000000f9396328 00000000f9396320 0000000000011100
| r28-31 0000000040cf2528 0000000000000010 00000000f9396400 00000000000106e7
| sr00-03 00000000003a7800 0000000000000000 0000000000000000 00000000003a7800
| sr04-07 00000000003a7800 00000000003a7800 00000000003a7800 00000000003a7800
|
| IASQ: 0000000000000000 0000000000000000 IAOQ: 0000000000000578 000000000000057c
| IIR: 0e8095dc ISR: 0000000000000000 IOR: 0000000000000002
| CPU: 0 CR30: 000000007cb68000 CR31: 0000000011111111
| ORIG_R28: 0000000000000000
| IAOQ[0]: 0x578
| IAOQ[1]: 0x57c
| RP(r2): 0x10987
| Backtrace:
|
| Kernel panic - not syncing: Kernel Fault
| ---[ end Kernel panic - not syncing: Kernel Fault
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: Testing the lws_compare_and_swap_2 syscall 2017-10-24 20:03 Testing the lws_compare_and_swap_2 syscall Christoph Biedl @ 2017-10-25 1:48 ` John David Anglin 2017-10-26 0:22 ` Christoph Biedl 0 siblings, 1 reply; 8+ messages in thread From: John David Anglin @ 2017-10-25 1:48 UTC (permalink / raw) To: Christoph Biedl; +Cc: linux-parisc On 2017-10-24, at 4:03 PM, Christoph Biedl wrote: > To start with, using the invalid value 4 as size parameter does not > return ENOSYS as I'd expect but crashes my system[2], using both 32 and > 64 bit kernel, no root privileges required. /* Check the validity of the size pointer */ subi,>>= 4, %r23, %r0 b,n lws_exit_nosys The condition in the subi instruction should be ">>". The branch is incorrectly nullified when %r23 is 4. Dave -- John David Anglin dave.anglin@bell.net ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-10-25 1:48 ` John David Anglin @ 2017-10-26 0:22 ` Christoph Biedl 2017-10-26 14:06 ` John David Anglin 2017-11-06 21:27 ` Helge Deller 0 siblings, 2 replies; 8+ messages in thread From: Christoph Biedl @ 2017-10-26 0:22 UTC (permalink / raw) To: linux-parisc John David Anglin wrote... > On 2017-10-24, at 4:03 PM, Christoph Biedl wrote: > > > To start with, using the invalid value 4 as size parameter does not > > return ENOSYS as I'd expect but crashes my system[2], using both 32 and > > 64 bit kernel, no root privileges required. > > /* Check the validity of the size pointer */ > subi,>>= 4, %r23, %r0 > b,n lws_exit_nosys > > The condition in the subi instruction should be ">>". The branch is incorrectly nullified when > %r23 is 4. Ups, way too obvious. This does the trick, or: Tested-By: Should I try to get a CVE number for this, or is parisc considered *that* historical nobody actually cares? Now, using the given cmpxchg2 function, the following code tests this LWS for 32bit: The first test is for *mem == *old, the second for *mem != *old. Is there anything wrong with this? uint32_t mem; uint32_t old; uint32_t new; mem = 1; old = 1; new = 3; cmpxchg2 (&mem, &old, &new, 2); if (mem == old) { printf ("PASS: mem unchanged\n"); } else { printf ("FAIL: got = 0x%x, expected = 0x%x\n", mem, old); } mem = 1; old = 3; new = 3; cmpxchg2 (&mem, &old, &new, 2); if (mem == new) { printf ("PASS: mem changed\n"); } else { printf ("FAIL: got = 0x%x, expected = 0x%x\n", mem, new); } My problem here: Both tests fail, and so do more complex ones that test the other data sizes as well. After staring at lws_compare_and_swap_2 a long time it seems there are two issues: First, there is more usage of ",ma" so an update of mem/r26 hits the wrong place. After that, all results are the wrong way around. I'm frightened to tell but I fear the logic in lws_compare_and_swap_2 is inverted in each and every place. I'm happy to be convinced otherwise. But for the time being it seems the patch below is an improvement. Status: My system boots and all my tests pass. However, I find smartd stalling in 100% CPU, might be coincidence. Now I'm putting some more load onto the box to see whether it's less crashy then it used to be the previous weeks. Aside, there is another ",ma" modifier in lws_compare_and_swap that I fail to understand. Haven't checked yet in detail yet, though. Cheers, Christoph --- a/src/arch/parisc/kernel/syscall.S +++ b/src/arch/parisc/kernel/syscall.S @@ -796,30 +796,30 @@ ldo 1(%r0),%r28 /* 8bit CAS */ -13: ldb,ma 0(%r26), %r29 - sub,= %r29, %r25, %r0 +13: ldb 0(%r26), %r29 + sub,<> %r29, %r25, %r0 b,n cas2_end -14: stb,ma %r24, 0(%r26) +14: stb %r24, 0(%r26) b cas2_end copy %r0, %r28 nop nop /* 16bit CAS */ -15: ldh,ma 0(%r26), %r29 - sub,= %r29, %r25, %r0 +15: ldh 0(%r26), %r29 + sub,<> %r29, %r25, %r0 b,n cas2_end -16: sth,ma %r24, 0(%r26) +16: sth %r24, 0(%r26) b cas2_end copy %r0, %r28 nop nop /* 32bit CAS */ -17: ldw,ma 0(%r26), %r29 - sub,= %r29, %r25, %r0 +17: ldw 0(%r26), %r29 + sub,<> %r29, %r25, %r0 b,n cas2_end -18: stw,ma %r24, 0(%r26) +18: stw %r24, 0(%r26) b cas2_end copy %r0, %r28 nop @@ -827,21 +827,22 @@ /* 64bit CAS */ #ifdef CONFIG_64BIT -19: ldd,ma 0(%r26), %r29 - sub,*= %r29, %r25, %r0 +19: ldd 0(%r26), %r29 + sub,<> %r29, %r25, %r0 b,n cas2_end -20: std,ma %r24, 0(%r26) +20: std %r24, 0(%r26) copy %r0, %r28 #else /* Compare first word */ 19: ldw 0(%r26), %r29 sub,= %r29, %r22, %r0 - b,n cas2_end + b,n cas2_64set /* Compare second word */ 20: ldw 4(%r26), %r29 - sub,= %r29, %r23, %r0 + sub,<> %r29, %r23, %r0 b,n cas2_end /* Perform the store */ +cas2_64set: 21: fstdx %fr4, 0(%r26) copy %r0, %r28 #endif ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-10-26 0:22 ` Christoph Biedl @ 2017-10-26 14:06 ` John David Anglin 2017-11-06 21:27 ` Helge Deller 1 sibling, 0 replies; 8+ messages in thread From: John David Anglin @ 2017-10-26 14:06 UTC (permalink / raw) To: Christoph Biedl, linux-parisc On 2017-10-25 8:22 PM, Christoph Biedl wrote: > After staring at lws_compare_and_swap_2 a long time it seems there are > two issues: First, there is more usage of ",ma" so an update of mem/r26 > hits the wrong place. The other uses of ",ma" are not a problem as the increment value is 0. So, the pointer is unchanged. The double word case had an increment of 4 messing up the pointer. Technically, ",ma" with a zero increment provides an ordered load. However, the completer probably isn't necessary as I believe all loads and stores are ordered on real hardware. The "=" completer in the "sub" instructions looks correct to me. When *mem and old are equal, the "b,n cas2_end" instruction is nullified and new is stored in mem. See comment from code: /* prev = *addr; if ( prev == old ) *addr = new; return prev; */ In your first test example, old and new should be exchanged in mem. Pass should be mem == new. Second case should be mem == old. Dave -- John David Anglin dave.anglin@bell.net ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-10-26 0:22 ` Christoph Biedl 2017-10-26 14:06 ` John David Anglin @ 2017-11-06 21:27 ` Helge Deller 2017-11-06 22:45 ` John David Anglin 2017-11-07 21:46 ` Christoph Biedl 1 sibling, 2 replies; 8+ messages in thread From: Helge Deller @ 2017-11-06 21:27 UTC (permalink / raw) To: Christoph Biedl, Richard Henderson, John David Anglin; +Cc: linux-parisc * Christoph Biedl <linux-kernel.bfrz@manchmal.in-ulm.de>: > John David Anglin wrote... > > > On 2017-10-24, at 4:03 PM, Christoph Biedl wrote: > > > > > To start with, using the invalid value 4 as size parameter does not > > > return ENOSYS as I'd expect but crashes my system[2], using both 32 and > > > 64 bit kernel, no root privileges required. > > > > /* Check the validity of the size pointer */ > > subi,>>= 4, %r23, %r0 > > b,n lws_exit_nosys > > > > The condition in the subi instruction should be ">>". The branch is incorrectly nullified when > > %r23 is 4. I'd prefer this (untested) patch: diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 41e60a9c7db2..f8a8754322ae 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -698,8 +698,7 @@ lws_compare_and_swap_2: #endif /* Check the validity of the size pointer */ - subi,>>= 4, %r23, %r0 - b,n lws_exit_nosys + cmpib,COND(<<),n 3, %r23, lws_exit_nosys /* Jump to the functions which will load the old and new values into registers depending on the their size */ > Ups, way too obvious. This does the trick, or: Tested-By: > Should I try to get a CVE number for this, or is parisc considered > *that* historical nobody actually cares? I think a CVE is not needed for parisc. There are no real productive users (I assume). > Now, using the given cmpxchg2 function, the following code tests this > LWS for 32bit: The first test is for *mem == *old, the second for > *mem != *old. Is there anything wrong with this? > > uint32_t mem; > uint32_t old; > uint32_t new; > > mem = 1; > old = 1; > new = 3; > cmpxchg2 (&mem, &old, &new, 2); > if (mem == old) { > printf ("PASS: mem unchanged\n"); > } else { > printf ("FAIL: got = 0x%x, expected = 0x%x\n", mem, old); > } > > mem = 1; > old = 3; > new = 3; > cmpxchg2 (&mem, &old, &new, 2); > if (mem == new) { > printf ("PASS: mem changed\n"); > } else { > printf ("FAIL: got = 0x%x, expected = 0x%x\n", mem, new); > } > > My problem here: Both tests fail, and so do more complex ones that test > the other data sizes as well. > > After staring at lws_compare_and_swap_2 a long time it seems there are > two issues: First, there is more usage of ",ma" so an update of mem/r26 > hits the wrong place. After that, all results are the wrong way around. > I'm frightened to tell but I fear the logic in lws_compare_and_swap_2 > is inverted in each and every place. I'm happy to be convinced > otherwise. But for the time being it seems the patch below is an > improvement. > > Status: My system boots and all my tests pass. However, I find smartd > stalling in 100% CPU, might be coincidence. Now I'm putting some more > load onto the box to see whether it's less crashy then it used to be the > previous weeks. Did you continued your tests? How were the results. > Aside, there is another ",ma" modifier in lws_compare_and_swap that I > fail to understand. Haven't checked yet in detail yet, though. I'd suggest to keep the other ",ma" modifiers for now. Helge ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-11-06 21:27 ` Helge Deller @ 2017-11-06 22:45 ` John David Anglin 2017-11-07 21:31 ` John David Anglin 2017-11-07 21:46 ` Christoph Biedl 1 sibling, 1 reply; 8+ messages in thread From: John David Anglin @ 2017-11-06 22:45 UTC (permalink / raw) To: Helge Deller, Christoph Biedl, Richard Henderson; +Cc: linux-parisc On 2017-11-06 4:27 PM, Helge Deller wrote: > /* Check the validity of the size pointer */ > - subi,>>= 4, %r23, %r0 > - b,n lws_exit_nosys > + cmpib,COND(<<),n 3, %r23, lws_exit_nosys I don't believe that we want to use COND here (i.e., we want a 32-bit check). We might not need to trim the upper 32-bits r23. The reason the code uses nullification is the fast path occurs when the "b,n" is nullified. So we avoid the branch prediction penalty. I'd have to check whether the fast path with the cmpib instruction is the taken branch or not. Have no object to changing "4" to "3" instead of changing the condition. Dave -- John David Anglin dave.anglin@bell.net ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-11-06 22:45 ` John David Anglin @ 2017-11-07 21:31 ` John David Anglin 0 siblings, 0 replies; 8+ messages in thread From: John David Anglin @ 2017-11-07 21:31 UTC (permalink / raw) To: Helge Deller, Christoph Biedl, Richard Henderson; +Cc: linux-parisc On 2017-11-06 5:45 PM, John David Anglin wrote: > On 2017-11-06 4:27 PM, Helge Deller wrote: >> /* Check the validity of the size pointer */ >> - subi,>>= 4, %r23, %r0 >> - b,n lws_exit_nosys >> + cmpib,COND(<<),n 3, %r23, lws_exit_nosys > I don't believe that we want to use COND here (i.e., we want a 32-bit > check). We might not > need to trim the upper 32-bits r23. > > The reason the code uses nullification is the fast path occurs when > the "b,n" is nullified. So we > avoid the branch prediction penalty. I'd have to check whether the > fast path with the cmpib instruction > is the taken branch or not. For cmpib with "<<" condition, the hint for a backward branch is likely taken. The branch to lws_exit_nosys is backward. Dave -- John David Anglin dave.anglin@bell.net ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Testing the lws_compare_and_swap_2 syscall 2017-11-06 21:27 ` Helge Deller 2017-11-06 22:45 ` John David Anglin @ 2017-11-07 21:46 ` Christoph Biedl 1 sibling, 0 replies; 8+ messages in thread From: Christoph Biedl @ 2017-11-07 21:46 UTC (permalink / raw) To: Helge Deller, Richard Henderson, John David Anglin, linux-parisc Helge Deller wrote... > * Christoph Biedl <linux-kernel.bfrz@manchmal.in-ulm.de>: > > Status: My system boots and all my tests pass. However, I find smartd > > stalling in 100% CPU, might be coincidence. Now I'm putting some more > > load onto the box to see whether it's less crashy then it used to be the > > previous weeks. > > Did you continued your tests? > How were the results. Basically I gave up on this. My change made things worse, and while I still didn't get the point (read: For my understanding there's a discrepancy between the syscall's description and what actually happens), it's certainly better to keep things the way they are. > > Aside, there is another ",ma" modifier in lws_compare_and_swap that I > > fail to understand. Haven't checked yet in detail yet, though. > > I'd suggest to keep the other ",ma" modifiers for now. Since, as I presume, this has no impact on performance, this boils down to aid a human reviewing the code in understanding what's happening here. Christoph ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-11-07 21:46 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-10-24 20:03 Testing the lws_compare_and_swap_2 syscall Christoph Biedl 2017-10-25 1:48 ` John David Anglin 2017-10-26 0:22 ` Christoph Biedl 2017-10-26 14:06 ` John David Anglin 2017-11-06 21:27 ` Helge Deller 2017-11-06 22:45 ` John David Anglin 2017-11-07 21:31 ` John David Anglin 2017-11-07 21:46 ` Christoph Biedl
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.