On Wed, Jul 01, 2020 at 11:53:05AM -0400, Nathan Sidwell wrote:
> On 7/1/20 10:11 AM, Gong, Sishuai via Gcc wrote:
> > Hope this mail finds you well. I am writing this to ask about one extension
> > in GCC, which is “Conditionals with Omitted Operands”.
> >
> > From the document at https://gcc.gnu.org/onlinedocs/gcc/Conditionals.html
> > , we learn that this extension could be useful in terms of avoiding the
> > side effects of recomputing. However, we recently observed a case in the
> > Linux kernel, where the kernel develops are using this extension but
> > compiling their code with certain optimizations disabled may lead to a
> > concurrency vulnerability. The general idea of this problem is, for a line
> > of code leveraging “Conditionals with Omitted Operands", GCC, with fewer
> > optimizations, could generate a disassembly contains two memory read to the
> > same object. One is for checking the value in the first operand in the
> > ternary expression and another is for the second operand, which is omitted
> > in order to leverage this extension. Thus, another thread could update the
> > object between the two read and lead to inconsistent behavior. We are not
> > sure if this is a problem with GCC or the kernel developers should be aware
> > of this vulnerability. Hope you could give us some hints.
> >
> > Here I put a simple program that I hope could explain this problem.
> > #include<stdio.h>
> > #include<stdlib.h>
> >
> > unsigned int hello(int *const *a)
> > {
> > return (unsigned int)((unsigned int)*a & 0xFE) ? : 0x123;
> > }
>
> because a's type is 'int *', there are no changes in semantics, at the C
> abstract machine level, for accessing *a any number of times. Thus
> depending on a specific number of accesses is unreliable.
Yeah, this is in no way related to ?: with omitted middle operand, e.g. with
register allocation a value from memory might be read many times rather than
just once even if in the source it is read once - the compiler sees the
value in some memory and if it can prove the value doesn't change in
between, rather than e.g. spilling it to a new stack slot it can just read
it again.
If you need guarantees, you should be using __atomic_load_n, or inline asm
that will ensure the value is only in a register (or could be spilled to
stack), but never reread from he containing memory, or you could use
volatile (that hurts other optimizations though).
Jakub