Open
Description
Nim Version
Nim Compiler Version 2.2.4 [Linux: amd64]
Compiled at 2025-06-08
Copyright (c) 2006-2025 by Andreas Rumpf
git hash: f7145dd26efeeeb6eeae6fff649db244d81b212d
active boot switches: -d:release
Nim Compiler Version 2.2.5 [Linux: amd64]
Compiled at 2025-06-15
Copyright (c) 2006-2025 by Andreas Rumpf
git hash: 7fdbdb2f20a9d43e62afac8c32dfd3a3e39d3149
active boot switches: -d:release
Nim Compiler Version 2.3.1 [Linux: amd64]
Compiled at 2025-06-15
Copyright (c) 2006-2025 by Andreas Rumpf
git hash: 7701b3c7e6f6c640a89cc445b40f466834ab4fcf
active boot switches: -d:release
On Debian unstable with:
gcc (Debian 14.2.0-19) 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.
Description
This is a somewhat involved one, but I wasn't able to get it under 4 distinct modules without losing the 100% reliability it has for me:
$ wc -l *.nim
20 e.nim
7 o.nim
26 t.nim
89 w.nim
142 total
Notably, exactly one of the files signals that it should not be using LTO. This reflects the actual use case, where it's important to be able to do this.
The main module, w.nim
:
import std/[macros, tables], ./o
proc y(): seq[seq[byte]] =
result = @[@[248'u8, 177, 160, 129, 136, 149, 159, 31, 252, 215, 147, 250, 28, 74, 127, 243, 250, 52, 43, 117, 253, 206, 185, 136, 179, 23, 70, 75, 37, 169, 40, 81, 139, 29, 85, 128, 160, 166, 92, 64, 107, 103, 166, 196, 94, 147, 183, 129, 212, 225, 123, 145, 5, 105, 226, 248, 243, 193, 9, 179, 25, 169, 168, 252, 112, 223, 115, 37, 41, 128, 160, 212, 49, 8, 53, 235, 82, 204, 21, 4, 254, 38, 152, 121, 245, 19, 127, 137, 243, 84, 79, 146, 233, 16, 10, 222, 19, 147, 71, 196, 38, 5, 6, 128, 128, 128, 128, 128, 160, 194, 171, 71, 247, 21, 130, 2, 59, 51, 27, 110, 162, 104, 73, 163, 174, 229, 43, 72, 28, 43, 246, 103, 5, 27, 137, 130, 21, 106, 1, 201, 49, 128, 128, 128, 128, 160, 198, 39, 225, 154, 149, 227, 112, 175, 149, 233, 24, 177, 216, 49, 194, 32, 227, 116, 223, 82, 202, 202, 87, 37, 129, 92, 198, 14, 198, 134, 161, 216, 128], @[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
import ./t
type B = ref object
v: B
proc x(t: var B, g: openArray[byte]) =
let g = @g
while t != nil:
discard getOrDefault(default(Table[seq[byte], int]), g)
t = t.v
macro a(): untyped =
result = newStmtList()
for _, _ in [""]: discard
type K = object
case kind: range[0 .. 2]
of 2: discard
of 1:
e: array[32, byte]
r: uint8
of 0: p: seq[byte]
proc s(f: L): Q[L] = Q[L](g: true, z: h(f))
proc n(d: L): Q[seq[byte]] =
try:
Q[seq[byte]](g: true, z: @(d.bytes.p(item(d).payload)))
except CatchableError:
Q[seq[byte]](g: false)
template k(s: untyped): untyped =
var result = newSeq[byte](len(s))
discard s[0]
result
template c[T](self: Q[T]): auto =
let w = self
case w.g
of false:
result = Q[K]()
return
of true:
w.z
proc m(w: L): Q[seq[byte]] = Q[seq[byte]](g: true, z: k(toOpenArray(w.bytes, w.position, 1)))
proc w(): array[32, byte] = discard
proc p(f: L, i: uint8): Q[K] =
var w = f
while true:
if i == 0:
discard n(c(s(w)))
return Q[K](g: true, z: K(kind: 0))
else:
let h = c(s(w))
if j(h):
if len(c(m(h))) > 32:
return Q[K]()
else:
w = h
else:
let d = len(c(n(h)))
if d == 32:
discard w()
return Q[K](g: true, z: K(kind: 1))
elif d == 0:
return Q[K](g: true, z: K(kind: 2))
return Q[K](g: false)
proc u(data: openArray[byte]): L = L(bytes: @data, position: 0)
proc v(g: seq[byte]) =
var e = 64'u8
while true:
var t = new B
x(t, [])
let node = g
let next = p(u(node), e).z
case next.kind
of 0, 2:
return
of 1:
e = next.r
discard {"": 0}.toTable()
v(y()[0])
import ./e
e.nim
:
import std/[os, sets]
type W = ref object
var s: seq[W]
var h: HashSet[string]
proc f(c: openArray[string]) =
var g: string
let _ = 0
for d in c:
if d == " " or d == " ":
g = d[3..^1]
else:
h.incl(d)
s.add(W())
proc w(): seq[string] =
for i in 1..paramCount():
result.add(paramStr(i))
f(w())
o.nim
:
{.localPassC: "-fno-lto".}
type Q*[T] = object
case g*: bool
of false:
discard
of true:
z*: T
t.nim
:
type
L* = object
bytes*: seq[byte]
position*: int
D = tuple[payload: Slice[int], _: int]
template p*(n: openArray[byte], s: Slice[int]): openArray[byte] =
if s.b >= len(n): raiseAssert ""
toOpenArray(n, s.a, s.b)
proc k(f: openArray[byte], t = 0): D =
if f[t] <= 0x7f:
raiseAssert ""
elif f[t] <= 0xb7:
(1 .. int(f[t] - 128), 0)
else:
(2 .. 178, 0)
proc item*(e: L): D = k(e.bytes, e.position)
proc j*(v: L): bool = v.bytes[v.position] >= byte(0xc0)
proc h*(x: L): L =
let m = item(x)
var p = k(x.bytes.p(m.payload.a .. m.payload.b)).payload
for _ in 0 ..< 1:
p = k(x.bytes.p(2 .. 178)).payload
L(bytes: @(x.bytes.p(2 .. 2 + p.b)), position: 0)
As a helper script to ensure the reliability of the reproduction:
#!/bin/bash
set -u
# Check if a command is provided
if [ -z "$1" ]; then
echo "Usage: $0 <command>"
exit 1
fi
COMMAND="$1" # The command to run
SUCCESS_COUNT=0
TOTAL_RUNS=3000
for ((i=1; i<=TOTAL_RUNS; i++)); do
# Run the command and redirect both stdout and stderr to /dev/null
{ $COMMAND; } > /dev/null 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
((SUCCESS_COUNT++))
fi
done
# Print the summary of SUCCESS_COUNT
echo "$COMMAND SUCCESS_COUNT: $SUCCESS_COUNT"
if [ $SUCCESS_COUNT -gt 100 ]; then
exit 1
else
exit 0
fi
to ensure it consistently does not run successfully in ORC/-d:release
/LTO.
The tests so far:
- doesn't depend on
--threads:on
or--threads:off
, crashes similarly. - doesn't crash with
refc
in any version, only ORC - doesn't crash without LTO, or if using LTO on all modules (remove the
no-lto
pragma) - doesn't crash without
-d:release
status-im/nim-unittest2#55 (comment) also documents some of this.
Current Output
$ while true; do ~/nim22/bin/nim c -o:$(mktemp) --nimcache:$(mktemp -d) -r w.nim && ~/nim22/bin/nim c -o:$(mktemp) --nimcache:$(mktemp -d) --mm:refc -d:release -r w.nim && ~/nim23/bin/nim c -o:$(mktemp) -r w.nim && ~/nim23/bin/nim c --hints:on --warnings:on -o:/tmp/n23t1 --nimcache:$(mktemp -d) -d:release w.nim && crashy /tmp/n23t1 && ~/nim22/bin/nim c -o:/tmp/n22t0 --nimcache:$(mktemp -d) -d:release --threads:off w.nim && crashy /tmp/n22t0 && ~/nim22/bin/nim c --o:/tmp/n22t1 -d:release --threads:on --nimcache:$(mktemp -d) w.nim && crashy /tmp/n22t1 && ~/nim23/bin/nim c -o:/tmp/n23t0 --nimcache:$(mktemp -d) -d:release --threads:off w.nim && crashy /tmp/n23t0; sleep 4; done
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
nim-unittest2/w.nim(16, 7) Hint: 'a' is declared but not used [XDeclaredButNotUsed]
nim-unittest2/w.nim(89, 8) Warning: imported and not used: 'e' [UnusedImport]
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
/tmp/n23t1 SUCCESS_COUNT: 0
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
/tmp/n22t0 SUCCESS_COUNT: 0
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
/tmp/n22t1 SUCCESS_COUNT: 0
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
/tmp/n23t0 SUCCESS_COUNT: 0
lto-wrapper: warning: using serial compilation of 2 LTRANS jobs
lto-wrapper: note: see the ‘-flto’ option documentation for more information
Or individually:
$ /tmp/n22t0
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault
$ /tmp/n22t1
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault
$ /tmp/n23t0
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault
$ /tmp/n23t1
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault
Or Valgrind:
$ valgrind /tmp/n22t0
==845673== Memcheck, a memory error detector
==845673== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==845673== Using Valgrind-3.24.0 and LibVEX; rerun with -h for copyright info
==845673== Command: /tmp/n22t0
==845673==
==845673== Conditional jump or move depends on uninitialised value(s)
==845673== at 0x10AAC3: eqdestroy___w_u42 (in /tmp/n22t0)
==845673== by 0x11429E: v__w_u1355 (in /tmp/n22t0)
==845673== by 0x109FD2: main (in /tmp/n22t0)
==845673==
==845673== Use of uninitialised value of size 8
==845673== at 0x10AAC5: eqdestroy___w_u42 (in /tmp/n22t0)
==845673== by 0x11429E: v__w_u1355 (in /tmp/n22t0)
==845673== by 0x109FD2: main (in /tmp/n22t0)
==845673==
==845673== Invalid read of size 1
==845673== at 0x10AAC5: eqdestroy___w_u42 (in /tmp/n22t0)
==845673== by 0x11429E: v__w_u1355 (in /tmp/n22t0)
==845673== by 0x109FD2: main (in /tmp/n22t0)
==845673== Address 0x2841f61 is not stack'd, malloc'd or (recently) free'd
==845673==
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
==845673==
==845673== Process terminating with default action of signal 11 (SIGSEGV)
==845673== at 0x48FE95C: __pthread_kill_implementation (pthread_kill.c:44)
==845673== by 0x48A9CC1: raise (raise.c:26)
==845673== by 0x48A9DEF: ??? (in /usr/lib/x86_64-linux-gnu/libc.so.6)
==845673== by 0x10AAC4: eqdestroy___w_u42 (in /tmp/n22t0)
==845673== by 0x11429E: v__w_u1355 (in /tmp/n22t0)
==845673== by 0x109FD2: main (in /tmp/n22t0)
==845673==
==845673== HEAP SUMMARY:
==845673== in use at exit: 0 bytes in 0 blocks
==845673== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==845673==
==845673== All heap blocks were freed -- no leaks are possible
==845673==
==845673== Use --track-origins=yes to see where uninitialised values come from
==845673== For lists of detected and suppressed errors, rerun with: -s
==845673== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault
$ valgrind /tmp/n23t1
==845777== Memcheck, a memory error detector
==845777== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==845777== Using Valgrind-3.24.0 and LibVEX; rerun with -h for copyright info
==845777== Command: /tmp/n23t1
==845777==
==845777== Conditional jump or move depends on uninitialised value(s)
==845777== at 0x10B643: eqdestroy___w_u42 (in /tmp/n23t1)
==845777== by 0x114EB6: v__w_u1355 (in /tmp/n23t1)
==845777== by 0x10A455: main (in /tmp/n23t1)
==845777==
==845777== Use of uninitialised value of size 8
==845777== at 0x10B645: eqdestroy___w_u42 (in /tmp/n23t1)
==845777== by 0x114EB6: v__w_u1355 (in /tmp/n23t1)
==845777== by 0x10A455: main (in /tmp/n23t1)
==845777==
==845777== Invalid read of size 1
==845777== at 0x10B645: eqdestroy___w_u42 (in /tmp/n23t1)
==845777== by 0x114EB6: v__w_u1355 (in /tmp/n23t1)
==845777== by 0x10A455: main (in /tmp/n23t1)
==845777== Address 0x2841f61 is not stack'd, malloc'd or (recently) free'd
==845777==
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
==845777==
==845777== Process terminating with default action of signal 11 (SIGSEGV)
==845777== at 0x48FE95C: __pthread_kill_implementation (pthread_kill.c:44)
==845777== by 0x48A9CC1: raise (raise.c:26)
==845777== by 0x48A9DEF: ??? (in /usr/lib/x86_64-linux-gnu/libc.so.6)
==845777== by 0x10B644: eqdestroy___w_u42 (in /tmp/n23t1)
==845777== by 0x114EB6: v__w_u1355 (in /tmp/n23t1)
==845777== by 0x10A455: main (in /tmp/n23t1)
==845777==
==845777== HEAP SUMMARY:
==845777== in use at exit: 0 bytes in 0 blocks
==845777== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==845777==
==845777== All heap blocks were freed -- no leaks are possible
==845777==
==845777== Use --track-origins=yes to see where uninitialised values come from
==845777== For lists of detected and suppressed errors, rerun with: -s
==845777== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault
$ valgrind /tmp/n23t0
==845811== Memcheck, a memory error detector
==845811== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==845811== Using Valgrind-3.24.0 and LibVEX; rerun with -h for copyright info
==845811== Command: /tmp/n23t0
==845811==
==845811== Conditional jump or move depends on uninitialised value(s)
==845811== at 0x10B603: eqdestroy___w_u42 (in /tmp/n23t0)
==845811== by 0x114DDE: v__w_u1355 (in /tmp/n23t0)
==845811== by 0x10A471: main (in /tmp/n23t0)
==845811==
==845811== Use of uninitialised value of size 8
==845811== at 0x10B605: eqdestroy___w_u42 (in /tmp/n23t0)
==845811== by 0x114DDE: v__w_u1355 (in /tmp/n23t0)
==845811== by 0x10A471: main (in /tmp/n23t0)
==845811==
==845811== Invalid read of size 1
==845811== at 0x10B605: eqdestroy___w_u42 (in /tmp/n23t0)
==845811== by 0x114DDE: v__w_u1355 (in /tmp/n23t0)
==845811== by 0x10A471: main (in /tmp/n23t0)
==845811== Address 0x2841f61 is not stack'd, malloc'd or (recently) free'd
==845811==
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
==845811==
==845811== Process terminating with default action of signal 11 (SIGSEGV)
==845811== at 0x48FE95C: __pthread_kill_implementation (pthread_kill.c:44)
==845811== by 0x48A9CC1: raise (raise.c:26)
==845811== by 0x48A9DEF: ??? (in /usr/lib/x86_64-linux-gnu/libc.so.6)
==845811== by 0x10B604: eqdestroy___w_u42 (in /tmp/n23t0)
==845811== by 0x114DDE: v__w_u1355 (in /tmp/n23t0)
==845811== by 0x10A471: main (in /tmp/n23t0)
==845811==
==845811== HEAP SUMMARY:
==845811== in use at exit: 0 bytes in 0 blocks
==845811== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==845811==
==845811== All heap blocks were freed -- no leaks are possible
==845811==
==845811== Use --track-origins=yes to see where uninitialised values come from
==845811== For lists of detected and suppressed errors, rerun with: -s
==845811== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault
Expected Output
No segmentation fault
Known Workarounds
No response
Additional Information
Metadata
Metadata
Assignees
Labels
No labels