https://bugs.kde.org/show_bug.cgi?id=523434
Bug ID: 523434
Summary: Memcheck: false "Conditional jump depends on
uninitialised value" for unsigned compares of
partially-defined words (clang bitfield idiom)
Classification: Developer tools
Product: valgrind
Version First unspecified
Reported In:
Platform: Other
OS: Other
Status: REPORTED
Severity: normal
Priority: NOR
Component: memcheck
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
Created attachment 194570
--> https://bugs.kde.org/attachment.cgi?id=194570&action=edit
a patch ready for git am
SUMMARY
On x86_64, Memcheck reports a false "Conditional jump or move depends on
uninitialised value(s)" for the extremely common idiom of testing a bitfield
that shares its storage word with bits that have not been written. Recent
clang/LLVM frontends emit this pattern pervasively, so under
--error-exitcode the report fires on essentially every C++ compilation,
including `int main(){}`.
The comparison is in fact fully determined by the defined bits, so the report
is spurious. Downstream reference: llvm/llvm-project#194147
(https://github.com/llvm/llvm-project/issues/194147).
--------------------------------------------------------------------------------
ROOT CAUSE
clang lowers a test such as `(word >> 48) != 0` (a 16-bit subfield living at
bit offset 48 of a 64-bit word) to the single unsigned comparison
word >=u (1 << 48)
Because the constant 1<<48 has 48 trailing zero bits, no low bit of `word`
can change the result of the unsigned comparison — it is exactly
`(word >> 48) != 0` regardless of the low 48 bits. In the LLVM frontend
those low bits are the never-written padding of a freshly bump-allocated
bitfield object (e.g. clang::FunctionProtoType's trailing bitfield word, and
similarly in clang::CodeGen::CodeGenModule), so they are legitimately
undefined — but they do not affect the branch.
Memcheck's shadow rule for Iop_CmpLT{32,64}U / Iop_CmpLE{32,64}U is the cheap
one:
mkPCastTo(Ity_I1, mkUifU(vx, vy))
i.e. the result is undefined if ANY input bit is undefined. That is sound but
imprecise, and it taints this branch. Note that
--expensive-definedness-checks does NOT help: it only refines CmpEQ/CmpNE and
Add/Sub, never the ordered unsigned comparisons (verified: yes/no/auto all
still report).
--------------------------------------------------------------------------------
STEPS TO REPRODUCE
$ echo 'int main(){ return 0; }' > t.cpp
$ clang++ -O2 -c t.cpp -o t.o # clang 21 or newer, x86_64
$ valgrind -q --error-exitcode=1 clang++ -O2 -c t.cpp -o t.o
A self-contained C reproducer that needs no clang (models the partially
defined word directly) is attached as the regression test
memcheck/tests/partial_bitfield_cmp.c.
--------------------------------------------------------------------------------
ACTUAL RESULT
==NNNN== Conditional jump or move depends on uninitialised value(s)
==NNNN== at 0x...: clang::FunctionProtoType::FunctionProtoType(...)
==NNNN== by 0x...: clang::ASTContext::getFunctionTypeInternal(...)
...
(exit code 1)
Hundreds of such reports per compilation on x86_64; none correspond to a real
defect.
EXPECTED RESULT
No error; the branch is fully determined by the defined bits.
--------------------------------------------------------------------------------
FIX (patch attached)
Add an exact "expensive" interpretation of unsigned ordered comparisons,
expensiveCmpLTorLEU(), that bounds each operand by the two extremal
instantiations of its undefined bits and reports undefined only when both
comparison outcomes are actually reachable:
xmax = x | vx xmin = xmax ^ vx (= x & ~vx)
ymax = y | vy ymin = ymax ^ vy
x <u y undefined iff (xmin <u ymax) && (ymin <=u xmax)
x <=u y undefined iff (xmin <=u ymax) && (ymin <u xmax)
Cost: 7 IROps (four bit ops, two compares, one And1), no Not. Gated behind a
new detail level dl_CmpLTU_CmpLEU and enabled by default on x86 and amd64
under EdcAUTO, exactly as the existing CmpEQ/CmpNE expensive rules are, since
it is those targets' LLVM-optimised code that triggers the false positives.
VALIDATION
* Exhaustive check of the extremal formula against a brute-force
undefined-ness oracle over all 4-bit operand/mask combinations
(2 x 65536 cases, CmpLTU and CmpLEU): zero mismatches.
* Stock-vs-patched differential over the memcheck test corpus (188 programs,
34 of which emit genuine uninitialised-value diagnostics): byte-identical
output — no real diagnostics are lost.
* New regression test memcheck/tests/partial_bitfield_cmp: fails (3 spurious
errors) before the patch, clean after.
* On x86_64 with clang 22: `int main(){}` and a small STL translation unit go
from 14 / 40 reported errors to 0 under default options.
Patch is git-am ready (against master); it also adds a NEWS entry and the
regression test.
--
You are receiving this mail because:
You are watching all bug changes.