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