Open tersec opened 1 month ago
It's not the compiler's fault if you get noinit
wrong. What can it do about it anyway.
How is this wrong?
{.noinit.}
is just conceptually incompatible with case objects, presumably.
What can it do about it anyway.
Notably, this has nothing to do with the case object feature - it applies equally to all types - var x: ref int {.noinit.}
is invalid in the exact same way as a case object with a ref member in one of its branches would be
I cannot reproduce the failure: nim c -r --mm:refc -d:release --passC="-fsanitize=undefined" --passL="-fsanitize=undefined" test23.nim
devel/2.2.0 and gcc 14 on Linux
It depends a lot on the specific constants. The basic issue is still there in the code structure -- it's reading garbage/unitialized memory.
For reference I'm using Debian sid
with
gcc (Debian 14.2.0-7) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Linux version 6.11.2-amd64 (debian-kernel@lists.debian.org) (x86_64-linux-gnu-gcc-14 (Debian 14.2.0-6) 14.2.0, GNU ld (GNU Binutils for Debian) 2.43.1) #1 SMP PREEMPT_DYNAMIC Debian 6.11.2-1 (2024-10-05)
The entire type
section is there to try to align things in memory for maximum effect, but it's not fundamentally the issue, it only makes it more visible. This is why I included the source code generated -- the combination of {.noinit.}
and certain types, including case objects, creates UB.
in the case of a non-trivial type, emit a warning or an error
And then you can disable the warning message effectively introducing .noinit
and .reallyNoInit
but this is a stupid design as .noinit
already means "I know better than the compiler". Sometimes sharp features can cut.
Description
Compile with
nim c -r --mm:refc -d:release --passC="-fsanitize=undefined" --passL="-fsanitize=undefined"
In particular, what appears to happen is that the
{.noinit.}
in a local sense as expected, but what doesn't happen is the assignment overwritingc
with a completely initialized object.From the compiled C, this manifest as this
s()
proc being translated into:and
u()
ass()
properly declaresbut indeed does not initialize it. So far, it's correct. The error is that
u()
, because it uses thisreturn style where it outputs directly to
Result
, reads from effectively uninitialized memory when determining what to do with thecase
object:rather than writing to it.
This
*Result
has never been initialized bys()
so it's garbage.Nim Version
Current Output
Expected Output
Known Workarounds
No response
Additional Information
Example
gdb
session (without UBSAN, i.e. it's not a UBSAN artifact, but with--debuginfo:on
):The exact alignment varies a bit depending on how it's run, but this
Result->w.r.w
is fromu()
per the description: s.nim.c.txt i.e. it's supposed to be one ofNIM_TRUE
orNIM_FALSE
because it's abool
discriminator for theK[T]
type.