diff mbox series

[v1] syscalls/signal06: add volatile to loop variable

Message ID 20220712173921.2623135-1-edliaw@google.com
State Changes Requested
Headers show
Series [v1] syscalls/signal06: add volatile to loop variable | expand

Commit Message

Edward Liaw July 12, 2022, 5:39 p.m. UTC
On Android compiled with clang, the loop variable will be optimized out
unless it is tagged with volatile.

Signed-off-by: Edward Liaw <edliaw@google.com>
---
 testcases/kernel/syscalls/signal/signal06.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Petr Vorel July 13, 2022, 12:48 p.m. UTC | #1
Hi Edward,

> On Android compiled with clang, the loop variable will be optimized out
> unless it is tagged with volatile.

Reviewed-by: Petr Vorel <pvorel@suse.cz>

Thanks!

Kind regards,
Petr
Cyril Hrubis July 19, 2022, 10:20 a.m. UTC | #2
Hi!
> On Android compiled with clang, the loop variable will be optimized out
> unless it is tagged with volatile.

Looking at the code it looks strange that it's optimized out since we
use the value for the loop as:

	loop = 0;

	D = VALUE;
	while (D == VALUE && loop < LOOPS) {
		asm(...);
		loop++;
	}

And the D variable is properly marked as volatile so it's not like the
loop can be expected to iterate preciselly LOOPS iterations.

It looks to me like the compiler actually forgets about the volatility
of D for some reason and then assumes that the loop does LOOPS
iterations.
Edward Liaw July 27, 2022, 9:37 p.m. UTC | #3
Hey Cyril, sorry for the late reply, I was on vacation.

On Tue, Jul 19, 2022 at 3:19 AM Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > On Android compiled with clang, the loop variable will be optimized out
> > unless it is tagged with volatile.
>
> Looking at the code it looks strange that it's optimized out since we
> use the value for the loop as:
>
>         loop = 0;
>
>         D = VALUE;
>         while (D == VALUE && loop < LOOPS) {
>                 asm(...);
>                 loop++;
>         }
>
> And the D variable is properly marked as volatile so it's not like the
> loop can be expected to iterate preciselly LOOPS iterations.
>
> It looks to me like the compiler actually forgets about the volatility
> of D for some reason and then assumes that the loop does LOOPS
> iterations.

I'm not totally sure how the compiler is working in this case.

What I saw with Android is that it fails with:
signal06    0  TINFO  :  loop = 2076174312
signal06    1  TFAIL  :
external/ltp/testcases/kernel/syscalls/signal/signal06.c:87: Bug
Reproduced!

It makes one iteration, then loop is set to a random large int and the
loop terminates.  Printing the value of loop inside the for loop
actually caused it to iterate 30000 times and succeed.

Compared to a successful run, which looks like:
signal06    0  TINFO  :  loop = 30000
signal06    1  TPASS  :  signal06 call succeeded

Thanks,
Edward
Edward Liaw Aug. 11, 2022, 3:24 p.m. UTC | #4
> > It looks to me like the compiler actually forgets about the volatility
> > of D for some reason and then assumes that the loop does LOOPS
> > iterations.

Hey Cyril,
I think I finally understand what you mean by this now; it is rather
strange that the volatility of D does not protect loop from being
optimized away by the compiler.  I don't have a good explanation as to
why it's happening but I'm not sure how to evaluate what's going on
either.  Should I do anything to move this patch forward?

Thanks,
Edward
Cyril Hrubis Aug. 11, 2022, 3:33 p.m. UTC | #5
Hi!
> I think I finally understand what you mean by this now; it is rather
> strange that the volatility of D does not protect loop from being
> optimized away by the compiler.  I don't have a good explanation as to
> why it's happening but I'm not sure how to evaluate what's going on
> either.  Should I do anything to move this patch forward?

It all boils down if we want to work around something that looks like a
compiler bug in tests or not. I would be inclined not to do so since LTP
was littered with quite a lot of workarounds for glibc/compiler bugs and
we spend quite some time cleaning that mess up. But in this case I can
agree that this is a borderland issue so I'm not strongly against that
either.
Petr Vorel Aug. 16, 2022, 12:43 p.m. UTC | #6
Hi Edward,

> Hi!
> > I think I finally understand what you mean by this now; it is rather
> > strange that the volatility of D does not protect loop from being
> > optimized away by the compiler.  I don't have a good explanation as to
> > why it's happening but I'm not sure how to evaluate what's going on
> > either.  Should I do anything to move this patch forward?

> It all boils down if we want to work around something that looks like a
> compiler bug in tests or not. I would be inclined not to do so since LTP
> was littered with quite a lot of workarounds for glibc/compiler bugs and
> we spend quite some time cleaning that mess up. But in this case I can
> agree that this is a borderland issue so I'm not strongly against that
> either.

Edward, which which clang version requires it? It'd be nice to document it, so
that it can be removed in the future.
Is there any bug report for it?

Kind regards,
Petr
Edward Liaw Aug. 16, 2022, 11 p.m. UTC | #7
We are currently building with clang 14.0.6.  I haven't filed a bug report
with llvm, will work on doing that.

On Tue, Aug 16, 2022 at 5:43 AM Petr Vorel <pvorel@suse.cz> wrote:

> Hi Edward,
>
> > Hi!
> > > I think I finally understand what you mean by this now; it is rather
> > > strange that the volatility of D does not protect loop from being
> > > optimized away by the compiler.  I don't have a good explanation as to
> > > why it's happening but I'm not sure how to evaluate what's going on
> > > either.  Should I do anything to move this patch forward?
>
> > It all boils down if we want to work around something that looks like a
> > compiler bug in tests or not. I would be inclined not to do so since LTP
> > was littered with quite a lot of workarounds for glibc/compiler bugs and
> > we spend quite some time cleaning that mess up. But in this case I can
> > agree that this is a borderland issue so I'm not strongly against that
> > either.
>
> Edward, which which clang version requires it? It'd be nice to document
> it, so
> that it can be removed in the future.
> Is there any bug report for it?
>
> Kind regards,
> Petr
>
Petr Vorel Aug. 17, 2022, 9:12 a.m. UTC | #8
Hi Edward,

> We are currently building with clang 14.0.6.  I haven't filed a bug report
> with llvm, will work on doing that.
Thanks for info. I expected it'd be for aarch64 arch, but I can reproduce it on
the same clang version on x86_64 on openSUSE Tumbleweed.

Would you mind we delay merging after you fill the bug in llvm, so that we can
add it to git commit message?

Tested-by: Petr Vorel <pvorel@suse.cz>

Kind regards,
Petr

> On Tue, Aug 16, 2022 at 5:43 AM Petr Vorel <pvorel@suse.cz> wrote:

> > Hi Edward,

> > > Hi!
> > > > I think I finally understand what you mean by this now; it is rather
> > > > strange that the volatility of D does not protect loop from being
> > > > optimized away by the compiler.  I don't have a good explanation as to
> > > > why it's happening but I'm not sure how to evaluate what's going on
> > > > either.  Should I do anything to move this patch forward?

> > > It all boils down if we want to work around something that looks like a
> > > compiler bug in tests or not. I would be inclined not to do so since LTP
> > > was littered with quite a lot of workarounds for glibc/compiler bugs and
> > > we spend quite some time cleaning that mess up. But in this case I can
> > > agree that this is a borderland issue so I'm not strongly against that
> > > either.

> > Edward, which which clang version requires it? It'd be nice to document
> > it, so
> > that it can be removed in the future.
> > Is there any bug report for it?

> > Kind regards,
> > Petr
Edward Liaw Aug. 17, 2022, 3:04 p.m. UTC | #9
On Wed, Aug 17, 2022, 2:12 AM Petr Vorel <pvorel@suse.cz> wrote:

> Hi Edward,
>
> > We are currently building with clang 14.0.6.  I haven't filed a bug
> report
> > with llvm, will work on doing that.
> Thanks for info. I expected it'd be for aarch64 arch, but I can reproduce
> it on
> the same clang version on x86_64 on openSUSE Tumbleweed.
>
> Would you mind we delay merging after you fill the bug in llvm, so that we
> can
> add it to git commit message?
>

Sure thing, not a problem.

>
Edward Liaw Aug. 18, 2022, 9:18 p.m. UTC | #10
>
> On Wed, Aug 17, 2022, 2:12 AM Petr Vorel <pvorel@suse.cz> wrote:
>
>> Hi Edward,
>>
>> Would you mind we delay merging after you fill the bug in llvm, so that
>> we can
>> add it to git commit message?
>
>
Here's the link to the bug on llvm:
https://github.com/llvm/llvm-project/issues/57234
Petr Vorel Aug. 19, 2022, 8:12 a.m. UTC | #11
> > On Wed, Aug 17, 2022, 2:12 AM Petr Vorel <pvorel@suse.cz> wrote:

> >> Hi Edward,

> >> Would you mind we delay merging after you fill the bug in llvm, so that
> >> we can
> >> add it to git commit message?


> Here's the link to the bug on llvm:
> https://github.com/llvm/llvm-project/issues/57234

Thanks! The bug was closed as 'adding "cx" worked'. Reading topperc's comment it
looks like there no other way to fix the issue on clang than workaround with
volatile. Does it mean that it's a syscall problem and clang can do nothing
about it?

Kind regards,
Petr
Cyril Hrubis Aug. 19, 2022, 8:41 a.m. UTC | #12
Hi!
> Thanks! The bug was closed as 'adding "cx" worked'. Reading topperc's comment it
> looks like there no other way to fix the issue on clang than workaround with
> volatile. Does it mean that it's a syscall problem and clang can do nothing
> about it?

It's problem with the inline assembly in the body of the while loop, the
call to the syscall changes the register value that is used for the D
variable in the case of clang, so the loop exits prematurely. We have to
add cx register to the clobber list for that asm statement so that
compiler knows that it's changed by the assembly.

Interfacing assembly with C is a bit tricky since you have to explain
to compiler which registers are changed from the assembly otherwise the
results are undefined.

The patch should look like:

diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
index 64f886ee3..78efd0fb9 100644
--- a/testcases/kernel/syscalls/signal/signal06.c
+++ b/testcases/kernel/syscalls/signal/signal06.c
@@ -73,7 +73,7 @@ void test(void)
                /* sys_tkill(pid, SIGHUP); asm to avoid save/reload
                 * fp regs around c call */
                asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP));
-               asm ("syscall" : : : "ax");
+               asm ("syscall" : : : "ax", "cx");

                loop++;
        }

Although it may not be a complete as the llwm issue suggests we should
have a look at calling conventions for the syscall and check if we need
to add any other registers to the clobber list.
Cyril Hrubis Aug. 19, 2022, 9:02 a.m. UTC | #13
On Fri, Aug 19, 2022 at 10:41:23AM +0200, Cyril Hrubis wrote:
> Hi!
> > Thanks! The bug was closed as 'adding "cx" worked'. Reading topperc's comment it
> > looks like there no other way to fix the issue on clang than workaround with
> > volatile. Does it mean that it's a syscall problem and clang can do nothing
> > about it?
> 
> It's problem with the inline assembly in the body of the while loop, the
> call to the syscall changes the register value that is used for the D
> variable in the case of clang, so the loop exits prematurely. We have to
> add cx register to the clobber list for that asm statement so that
> compiler knows that it's changed by the assembly.
> 
> Interfacing assembly with C is a bit tricky since you have to explain
> to compiler which registers are changed from the assembly otherwise the
> results are undefined.
> 
> The patch should look like:
> 
> diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
> index 64f886ee3..78efd0fb9 100644
> --- a/testcases/kernel/syscalls/signal/signal06.c
> +++ b/testcases/kernel/syscalls/signal/signal06.c
> @@ -73,7 +73,7 @@ void test(void)
>                 /* sys_tkill(pid, SIGHUP); asm to avoid save/reload
>                  * fp regs around c call */
>                 asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP));
> -               asm ("syscall" : : : "ax");
> +               asm ("syscall" : : : "ax", "cx");
> 
>                 loop++;
>         }
> 
> Although it may not be a complete as the llwm issue suggests we should
> have a look at calling conventions for the syscall and check if we need
> to add any other registers to the clobber list.

Looking at: https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux

We may as well add r11 as suggested by the clang issue comment and that
should be complete.

Edward will you send a patch?
Joerg Vehlow Aug. 19, 2022, 9:14 a.m. UTC | #14
Hi,

Am 8/19/2022 um 10:41 AM schrieb Cyril Hrubis:
> Hi!
>> Thanks! The bug was closed as 'adding "cx" worked'. Reading topperc's comment it
>> looks like there no other way to fix the issue on clang than workaround with
>> volatile. Does it mean that it's a syscall problem and clang can do nothing
>> about it?
> 
> It's problem with the inline assembly in the body of the while loop, the
> call to the syscall changes the register value that is used for the D
> variable in the case of clang, so the loop exits prematurely. We have to
> add cx register to the clobber list for that asm statement so that
> compiler knows that it's changed by the assembly.
> 
> Interfacing assembly with C is a bit tricky since you have to explain
> to compiler which registers are changed from the assembly otherwise the
> results are undefined.
> 
> The patch should look like:
> 
> diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
> index 64f886ee3..78efd0fb9 100644
> --- a/testcases/kernel/syscalls/signal/signal06.c
> +++ b/testcases/kernel/syscalls/signal/signal06.c
> @@ -73,7 +73,7 @@ void test(void)
>                 /* sys_tkill(pid, SIGHUP); asm to avoid save/reload
>                  * fp regs around c call */
>                 asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP));
> -               asm ("syscall" : : : "ax");
> +               asm ("syscall" : : : "ax", "cx");
Why is this even split up into two asm instructions?
I guess there is nothing, that prevents the compiler from reordering the
asm instructions, because it does not know, that they have side effects
(they are not marked volatile).

asm volatile ("syscall" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP):
"rax", "rcx", "r11");


I am not sure if there is any good reason, to split this up into two asm
instructions and if there is a good reason, to use the short names of
the registers.

Joerg
Cyril Hrubis Aug. 19, 2022, 9:21 a.m. UTC | #15
Hi!
> > It's problem with the inline assembly in the body of the while loop, the
> > call to the syscall changes the register value that is used for the D
> > variable in the case of clang, so the loop exits prematurely. We have to
> > add cx register to the clobber list for that asm statement so that
> > compiler knows that it's changed by the assembly.
> > 
> > Interfacing assembly with C is a bit tricky since you have to explain
> > to compiler which registers are changed from the assembly otherwise the
> > results are undefined.
> > 
> > The patch should look like:
> > 
> > diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
> > index 64f886ee3..78efd0fb9 100644
> > --- a/testcases/kernel/syscalls/signal/signal06.c
> > +++ b/testcases/kernel/syscalls/signal/signal06.c
> > @@ -73,7 +73,7 @@ void test(void)
> >                 /* sys_tkill(pid, SIGHUP); asm to avoid save/reload
> >                  * fp regs around c call */
> >                 asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP));
> > -               asm ("syscall" : : : "ax");
> > +               asm ("syscall" : : : "ax", "cx");
> Why is this even split up into two asm instructions?
> I guess there is nothing, that prevents the compiler from reordering the
> asm instructions, because it does not know, that they have side effects
> (they are not marked volatile).
> 
> asm volatile ("syscall" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP):
> "rax", "rcx", "r11");
> 
> 
> I am not sure if there is any good reason, to split this up into two asm
> instructions and if there is a good reason, to use the short names of
> the registers.

I was wondering too, I guess it's a direct copy of a reproducer from a
kernel commit see:

https://lore.kernel.org/lkml/1414436240-13879-8-git-send-email-kamal@canonical.com/
Petr Vorel Aug. 19, 2022, 10:40 a.m. UTC | #16
> Hi!
> > Thanks! The bug was closed as 'adding "cx" worked'. Reading topperc's comment it
> > looks like there no other way to fix the issue on clang than workaround with
> > volatile. Does it mean that it's a syscall problem and clang can do nothing
> > about it?

> It's problem with the inline assembly in the body of the while loop, the
> call to the syscall changes the register value that is used for the D
> variable in the case of clang, so the loop exits prematurely. We have to
> add cx register to the clobber list for that asm statement so that
> compiler knows that it's changed by the assembly.

> Interfacing assembly with C is a bit tricky since you have to explain
> to compiler which registers are changed from the assembly otherwise the
> results are undefined.

> The patch should look like:

> diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
> index 64f886ee3..78efd0fb9 100644
> --- a/testcases/kernel/syscalls/signal/signal06.c
> +++ b/testcases/kernel/syscalls/signal/signal06.c
> @@ -73,7 +73,7 @@ void test(void)
>                 /* sys_tkill(pid, SIGHUP); asm to avoid save/reload
>                  * fp regs around c call */
>                 asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP));
> -               asm ("syscall" : : : "ax");
> +               asm ("syscall" : : : "ax", "cx");

OK, I completely overlooked asm() in the test. Thanks!

Petr

>                 loop++;
>         }

> Although it may not be a complete as the llwm issue suggests we should
> have a look at calling conventions for the syscall and check if we need
> to add any other registers to the clobber list.
Edward Liaw Aug. 19, 2022, 6:13 p.m. UTC | #17
On Fri, Aug 19, 2022 at 2:14 AM Joerg Vehlow <lkml@jv-coder.de> wrote:

> Why is this even split up into two asm instructions?
> I guess there is nothing, that prevents the compiler from reordering the
> asm instructions, because it does not know, that they have side effects
> (they are not marked volatile).
>
> asm volatile ("syscall" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP):
> "rax", "rcx", "r11");
>

I tried this and clang complains that the "Asm-specifier for input or
output variable conflicts with asm clobber list" for rax.  Should it be

  asm volatile ("syscall" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP) :
"rcx", "r11");

instead?  Is it implicit that __NR_tkill is going into rax so it will be
clobbered?

Thanks,
Edward
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/signal/signal06.c b/testcases/kernel/syscalls/signal/signal06.c
index 64f886ee3..b40ff3e40 100644
--- a/testcases/kernel/syscalls/signal/signal06.c
+++ b/testcases/kernel/syscalls/signal/signal06.c
@@ -65,7 +65,7 @@  char altstack[4096 * 10] __attribute__((aligned(4096)));
 
 void test(void)
 {
-	int loop = 0;
+	volatile int loop = 0;
 	int pid = getpid();
 
 	D = VALUE;