[BPF] Check every locked atomic insn for a used result (#231550)
With `-mcpu=v3` and later, a monotonic (relaxed) `atomicrmw add`, `sub`,
`and`,
`or` or `xor` whose result is used is selected to the locked insn, which
does
not return the old value, so the program gets the operand register back
instead. The verifier accepts the program and it silently computes the
wrong
value: `next()` below returns 1 on every call instead of the previous
value of
`counter`, so anything built on it, such as an id or sequence counter,
breaks
without any error at compile or load time.
```c
long counter;
long next(void) {
return __atomic_fetch_add(&counter, 1, __ATOMIC_RELAXED);
}
```
Before:
```
r0 = 1
r1 = counter ll
lock *(u64 *)(r1 + 0) += r0
exit
```
After:
```
r0 = 1
r1 = counter ll
r0 = atomic_fetch_add((u64 *)(r1 + 0), r0)
exit
```
Commit 4c4fb6ada7a1 (#107343) intended the locked insn for monotonic
ordering
only when the result is unused, through the
`binop_no_use`/`binop_has_use`
fragments, but only used them for the 64-bit `and`, `or` and `xor`. Use
them
for the 32-bit `add`, `sub`, `and`, `or`, `xor` and the 64-bit `add` and
`sub`
as well. The locked insn is still selected when the result is unused,
and the
`BPFNoALU32` patterns are unchanged: without alu32 there is no fetching
add
insn, and `BPFMIChecking` reports an error when the result of the locked
insn
is used.
`BPFMIChecking` only checked `XADDW` and `XADDD`, and only without
jmp32, so
it never ran for v3+ and did not catch this. A live result of a locked
insn is
a selection bug on every cpu, so check all of the locked insns
regardless of
the cpu version.
`atomics_mem_order_v3.ll` was generated from the wrong output and
asserts it in
seven of its ten `*_ret` functions; regenerate it and also run it with
`-mcpu=v4`. Add `atomics_relaxed_alu32.ll`, covering every op and width
with
the result used and unused for `-mcpu=v1/v2 -mattr=+alu32` and
`-mcpu=v3/v4`,
and a MIR test that the checker reports each locked insn.