Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 941890 - net-analyzer/wireshark: fails tests with LTO
Summary: net-analyzer/wireshark: fails tests with LTO
Status: CONFIRMED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: Current packages (show other bugs)
Hardware: All Linux
: Normal normal
Assignee: Sam James
URL:
Whiteboard:
Keywords: TESTFAILURE
Depends on:
Blocks: lto
  Show dependency tree
 
Reported: 2024-10-20 14:09 UTC by Sam James
Modified: 2025-03-06 18:01 UTC (History)
3 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-20 14:09:33 UTC
See https://gitlab.com/wireshark/wireshark/-/issues/18216. Upstream have worked around it recently by adding -fno-delete-null-pointer-checks.

--

$ gdb --args /home/sam/git/wireshark/build/run/wmem_test -r /wmem/datastruct/tree
Reading symbols from /home/sam/git/wireshark/build/run/wmem_test...
(gdb) r
Starting program: /home/sam/git/wireshark/build/run/wmem_test -r /wmem/datastruct/tree
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
TAP version 13
# random seed: R02Se575e43e0b730f62f2f3ae6f6e419470
# Start of wmem tests
# Start of datastruct tests
=================================================================
==1433924==ERROR: AddressSanitizer: heap-use-after-free on address 0x5080002b11c8 at pc 0x555555556e38 bp 0x7fffffffd090 sp 0x7fffffffd080
READ of size 8 at 0x5080002b11c8 thread T0
    #0 0x555555556e37 in wmem_tree_foreach_nodes /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:755
    #1 0x555555573e34 in wmem_tree_foreach /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:789
    #2 0x555555573e34 in wmem_tree_count /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:316
    #3 0x555555573e34 in wmem_test_tree /home/sam/git/wireshark/wsutil/wmem/wmem_test.c:1238
    #4 0x7ffff772075b in test_case_run ../glib-2.78.6/glib/gtestutils.c:3161
    #5 0x7ffff772075b in g_test_run_suite_internal ../glib-2.78.6/glib/gtestutils.c:3256
    #6 0x7ffff77204ba in g_test_run_suite_internal ../glib-2.78.6/glib/gtestutils.c:3273
    #7 0x7ffff77204ba in g_test_run_suite_internal ../glib-2.78.6/glib/gtestutils.c:3273
    #8 0x7ffff7720f4a in g_test_run_suite ../glib-2.78.6/glib/gtestutils.c:3352
    #9 0x7ffff772102b in g_test_run ../glib-2.78.6/glib/gtestutils.c:2462
    #10 0x7ffff772102b in g_test_run ../glib-2.78.6/glib/gtestutils.c:2449
    #11 0x5555555549d1 in main /home/sam/git/wireshark/wsutil/wmem/wmem_test.c:1478
    #12 0x7ffff73db746 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #13 0x7ffff73db7f6 in __libc_start_main_impl ../csu/libc-start.c:360
    #14 0x555555554ae0 in _start (/home/sam/git/wireshark/build/run/wmem_test+0xae0)

0x5080002b11c8 is located 40 bytes inside of 88-byte region [0x5080002b11a0,0x5080002b11f8)
freed by thread T0 here:
    #0 0x7ffff79234db in free /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x55555555b44b in wmem_strict_free_all /home/sam/git/wireshark/wsutil/wmem/wmem_allocator_strict.c:182

previously allocated by thread T0 here:
    #0 0x7ffff792482b in malloc /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x7ffff76ee1ad in g_malloc ../glib-2.78.6/glib/gmem.c:130

SUMMARY: AddressSanitizer: heap-use-after-free /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:755 in wmem_tree_foreach_nodes
Shadow bytes around the buggy address:
  0x5080002b0f00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b0f80: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1000: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1080: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1100: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
=>0x5080002b1180: fa fa fa fa fd fd fd fd fd[fd]fd fd fd fd fd fa
  0x5080002b1200: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1280: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1300: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1380: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x5080002b1400: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==1433924==ABORTING

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at pthread_kill.c:44
44            return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
(gdb) bt
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at pthread_kill.c:78
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at pthread_kill.c:89
#3  0x00007ffff73f68c2 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff73d9832 in __GI_abort () at abort.c:79
#5  0x00007ffff794f0ad in __sanitizer::Abort () at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp:143
#6  0x00007ffff7961de8 in __sanitizer::Die () at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/sanitizer_common/sanitizer_termination.cpp:58
#7  0x00007ffff7931ad1 in __asan::ScopedInErrorReport::~ScopedInErrorReport (this=0x7fffffffc416)
    at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_report.cpp:192
#8  0x00007ffff7931289 in __asan::ReportGenericError (pc=93824992243256, bp=bp@entry=140737488343184, sp=sp@entry=140737488343168, addr=88510688858568, is_write=is_write@entry=false,
    access_size=8, fatal=true, exp=<optimized out>) at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_report.cpp:497
#9  0x00007ffff7931416 in __asan::ReportGenericError (pc=<optimized out>, bp=bp@entry=140737488343184, sp=sp@entry=140737488343168, addr=<optimized out>, is_write=is_write@entry=false,
    access_size=access_size@entry=8, exp=<optimized out>, fatal=true) at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_report.cpp:497
#10 0x00007ffff79326f5 in __asan::__asan_report_load8 (addr=<optimized out>) at /usr/src/debug/sys-devel/gcc-15.0.9999/gcc-15.0.9999/libsanitizer/asan/asan_rtl.cpp:131
#11 0x0000555555556e38 in wmem_tree_foreach_nodes (node=node@entry=0x5080002b11c0, callback=callback@entry=0x555555554c90 <count_nodes>, user_data=user_data@entry=0x7ffff4809050)
    at /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:755
#12 0x0000555555573e35 in wmem_tree_foreach (tree=0x507000000200, callback=0x555555554c90 <count_nodes>, user_data=0x7ffff4809050) at /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:789
#13 wmem_tree_count (tree=0x507000000200) at /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:316
#14 wmem_test_tree () at /home/sam/git/wireshark/wsutil/wmem/wmem_test.c:1238
#15 0x00007ffff772075c in test_case_run (tc=<optimized out>) at ../glib-2.78.6/glib/gtestutils.c:3161
#16 g_test_run_suite_internal (suite=suite@entry=0x503000001810, path=0x7fffffffdc11 "/wmem/datastruct/tree") at ../glib-2.78.6/glib/gtestutils.c:3256
#17 0x00007ffff77204bb in g_test_run_suite_internal (suite=suite@entry=0x5030000014e0, path=0x7fffffffdc11 "/wmem/datastruct/tree") at ../glib-2.78.6/glib/gtestutils.c:3273
#18 0x00007ffff77204bb in g_test_run_suite_internal (suite=suite@entry=0x503000001450, path=0x7fffffffdc11 "/wmem/datastruct/tree") at ../glib-2.78.6/glib/gtestutils.c:3273
#19 0x00007ffff7720f4b in g_test_run_suite (suite=0x503000001450) at ../glib-2.78.6/glib/gtestutils.c:3352
#20 0x00007ffff772102c in g_test_run () at ../glib-2.78.6/glib/gtestutils.c:2462
#21 g_test_run () at ../glib-2.78.6/glib/gtestutils.c:2449
#22 0x00005555555549d2 in main (argc=<optimized out>, argv=<optimized out>) at /home/sam/git/wireshark/wsutil/wmem/wmem_test.c:1478
(gdb) frame 11
#11 0x0000555555556e38 in wmem_tree_foreach_nodes (node=node@entry=0x5080002b11c0, callback=callback@entry=0x555555554c90 <count_nodes>, user_data=user_data@entry=0x7ffff4809050)
    at /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:755
755         if (node->left) {
(gdb) p node
$1 = (wmem_tree_node_t *) 0x5080002b11c0
(gdb) p *node
$2 = {
  parent = 0x1a1a1a1a1a1a1a1a,
  left = 0x1a1a1a1a1a1a1a1a,
  right = 0x1a1a1a1a1a1a1a1a,
  key = 0x1a1a1a1a1a1a1a1a,
  data = 0x1a1a1a1a1a1a1a1a,
  color = (unknown: 0x1a1a1a1a),
  is_subtree = 26,
  is_removed = 26
}
Comment 1 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-20 14:10:29 UTC
(gdb) frame 12
#12 0x0000555555573e35 in wmem_tree_foreach (tree=0x507000000200, callback=0x555555554c90 <count_nodes>, user_data=0x7ffff4809050) at /home/sam/git/wireshark/wsutil/wmem/wmem_tree.c:789
warning: Source file is more recent than executable.
789         return wmem_tree_foreach_nodes(tree->root, callback, user_data

tree->root here is null but the check was removed
Comment 2 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-20 14:10:42 UTC
GCC 10 works, 11 doesn't, bisecting.
Comment 3 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-20 15:50:59 UTC
Started with r11-5391-gbb07490abba850
Comment 4 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-20 16:14:15 UTC
For completeness as well:
```
$ valgrind -q --track-origins=yes --expensive-definedness-checks=yes /home/sam/git/wireshark/build/run/wmem_test -r /wmem/datastruct/tree       17:13:50 [35/9734]
TAP version 13
# random seed: R02Seb9be78c033ae982ce8e953bddf09563
# Start of wmem tests
# Start of datastruct tests
==46919== Invalid read of size 8
==46919==    at 0x10927D: wmem_tree_foreach_nodes (wmem_tree.c:755)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:789)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:316)
==46919==    by 0x114370: wmem_test_tree (wmem_test.c:1238)
==46919==    by 0x4A2C75B: UnknownInlinedFun (gtestutils.c:3161)
==46919==    by 0x4A2C75B: g_test_run_suite_internal (gtestutils.c:3256)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2CF4A: g_test_run_suite (gtestutils.c:3352)
==46919==    by 0x4A2D02B: UnknownInlinedFun (gtestutils.c:2462)
==46919==    by 0x4A2D02B: g_test_run (gtestutils.c:2449)
==46919==    by 0x10878C: main (wmem_test.c:1478)
==46919==  Address 0x5a7ea78 is 40 bytes inside a block of size 88 free'd
==46919==    at 0x484BDEF: free (vg_replace_malloc.c:989)
==46919==    by 0x10ACCB: wmem_strict_free_all (wmem_allocator_strict.c:182)
==46919==    by 0x114354: UnknownInlinedFun (wmem_core.c:110)
==46919==    by 0x114354: wmem_test_tree (wmem_test.c:1237)
==46919==    by 0x4A2C75B: UnknownInlinedFun (gtestutils.c:3161)
==46919==    by 0x4A2C75B: g_test_run_suite_internal (gtestutils.c:3256)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2CF4A: g_test_run_suite (gtestutils.c:3352)
==46919==    by 0x4A2D02B: UnknownInlinedFun (gtestutils.c:2462)
==46919==    by 0x4A2D02B: g_test_run (gtestutils.c:2449)
==46919==    by 0x10878C: main (wmem_test.c:1478)
==46919==  Block was alloc'd at
==46919==    at 0x4848B93: malloc (vg_replace_malloc.c:446)
==46919==    by 0x49FA1AD: g_malloc (gmem.c:130)
==46919==    by 0x109DBF: UnknownInlinedFun (wmem_core.c:35)
==46919==    by 0x109DBF: wmem_strict_alloc (wmem_allocator_strict.c:81)
==46919==    by 0x109394: UnknownInlinedFun (wmem_core.c:44)
==46919==    by 0x109394: UnknownInlinedFun (wmem_tree.c:327)
==46919==    by 0x109394: lookup_or_insert32_node (wmem_tree.c:393)
==46919==    by 0x114297: UnknownInlinedFun (wmem_tree.c:412)
==46919==    by 0x114297: UnknownInlinedFun (wmem_tree.c:498)
==46919==    by 0x114297: wmem_test_tree (wmem_test.c:1233)
==46919==    by 0x4A2C75B: UnknownInlinedFun (gtestutils.c:3161)
==46919==    by 0x4A2C75B: g_test_run_suite_internal (gtestutils.c:3256)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2CF4A: g_test_run_suite (gtestutils.c:3352)
==46919==    by 0x4A2D02B: UnknownInlinedFun (gtestutils.c:2462)
==46919==    by 0x4A2D02B: g_test_run (gtestutils.c:2449)
==46919==    by 0x10878C: main (wmem_test.c:1478)
==46919==
==46919== Invalid read of size 8
==46919==    at 0x10927D: wmem_tree_foreach_nodes (wmem_tree.c:755)
==46919==    by 0x109290: wmem_tree_foreach_nodes (wmem_tree.c:756)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:789)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:316)
==46919==    by 0x114370: wmem_test_tree (wmem_test.c:1238)
==46919==    by 0x4A2C75B: UnknownInlinedFun (gtestutils.c:3161)
==46919==    by 0x4A2C75B: g_test_run_suite_internal (gtestutils.c:3256)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2CF4A: g_test_run_suite (gtestutils.c:3352)
==46919==    by 0x4A2D02B: UnknownInlinedFun (gtestutils.c:2462)
==46919==    by 0x4A2D02B: g_test_run (gtestutils.c:2449)
==46919==    by 0x10878C: main (wmem_test.c:1478)
==46919==  Address 0x1a1a1a1a1a1a1a22 is not stack'd, malloc'd or (recently) free'd
==46919==
==46919==
==46919== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==46919==  General Protection Fault
==46919==    at 0x10927D: wmem_tree_foreach_nodes (wmem_tree.c:755)
==46919==    by 0x109290: wmem_tree_foreach_nodes (wmem_tree.c:756)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:789)
==46919==    by 0x114370: UnknownInlinedFun (wmem_tree.c:316)
==46919==    by 0x114370: wmem_test_tree (wmem_test.c:1238)
==46919==    by 0x4A2C75B: UnknownInlinedFun (gtestutils.c:3161)
==46919==    by 0x4A2C75B: g_test_run_suite_internal (gtestutils.c:3256)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2C4BA: g_test_run_suite_internal (gtestutils.c:3273)
==46919==    by 0x4A2CF4A: g_test_run_suite (gtestutils.c:3352)
==46919==    by 0x4A2D02B: UnknownInlinedFun (gtestutils.c:2462)
==46919==    by 0x4A2D02B: g_test_run (gtestutils.c:2449)
==46919==    by 0x10878C: main (wmem_test.c:1478)
Segmentation fault         (core dumped) valgrind -q --track-origins=yes --expensive-definedness-checks=yes /home/sam/git/wireshark/build/run/wmem_test -r /wmem/datastruct/tree
```
Comment 5 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2024-10-22 06:08:50 UTC
```
 /**
  * return inserted node
  */
+__attribute__((noipa))
 static wmem_tree_node_t *
 lookup_or_insert32_node(wmem_tree_t *tree, uint32_t key,
         void*(*func)(void*), void* data, bool is_subtree, bool replace)

```

is enough to suppress it (first had rb_insert_case1+lookup_or_insert32_node surrounded with no-sa pragma)
Comment 6 Holger Hoffstätte 2025-03-06 10:08:18 UTC
Sam's linked MR was merged, so I decided to retry with LTO.

- I added the MR patch to 4.4.5 (since we know 4.4.5 passes all tests)
- disabled filter-lto, explicitly enabled -DENABLE_LTO=ON
  (this should probably be automagic)
- enabled LTO via package.env

This built correctly using gcc-14.2.1_p20250301.

Both executable and libwireshark are quite a bit smaller, and the
resulting executable seems to work fine: I started a capture,
drilled down into packets etc. without problem, no crashes.

Running the test suite says:

  === 837 passed, 62 skipped, 5 warnings in 25.99s ====

Looks good!
Comment 7 Holger Hoffstätte 2025-03-06 10:12:00 UTC
*** Bug 754021 has been marked as a duplicate of this bug. ***
Comment 8 Holger Hoffstätte 2025-03-06 11:31:02 UTC
(In reply to Holger Hoffstätte from comment #6)
> - disabled filter-lto, explicitly enabled -DENABLE_LTO=ON
>   (this should probably be automagic)

Maybe something like

    if tc-is-lto; then
        mycmakeargs+=( -DENABLE_LTO=ON )
    fi

Seems to work. Not sure if it is even necessary.
Comment 9 Eli Schwartz gentoo-dev 2025-03-06 16:14:24 UTC
As far as I can tell there is zero purpose of -DENABLE_LTO=ON, since all it does is compile a test program to see whether the compiler implements LTO, then update the CMAKE_BUILD_TYPE=Release / RelWithDebInfo flags to utilize LTO.

While technically this doesn't "matter" for the eclass defaults, translating -flto into ENABLE_LTO would do one of two things:

- cause -flto to be passed multiple times, if you don't then filter-lto
- break users who configure CMAKE_BUILD_TYPE manually, if you do filter-lto
Comment 10 Holger Hoffstätte 2025-03-06 16:37:34 UTC
(In reply to Eli Schwartz from comment #9)
> As far as I can tell there is zero purpose of -DENABLE_LTO=ON, since all it
> does is compile a test program to see whether the compiler implements LTO,
> then update the CMAKE_BUILD_TYPE=Release / RelWithDebInfo flags to utilize
> LTO.

Yup, saw that. My idea here was that IF the user has enabled LTO we should at least try to play nice with the build system. It seemed odd to me that enabling LTO would let cmake's configure phase say that LTO is disabled, even if normal emerge runs will not show this.

> While technically this doesn't "matter" for the eclass defaults, translating
> -flto into ENABLE_LTO would do one of two things:
> 
> - cause -flto to be passed multiple times, if you don't then filter-lto

We want to get rid of filter-lto, and I verified that it does not pass -flto multiple times during compilation; it uses exactly the values I set in package.env. Either way the output is correct and what one would expect.

I was more confused that it's apparently not possible to use an inline expression like -DENABLE_LTO=$(tc-is-lto) - which is used in the tree but apparently does not work at all, hence the conditional block.
Comment 11 Eli Schwartz gentoo-dev 2025-03-06 18:01:07 UTC
(In reply to Holger Hoffstätte from comment #10)
> (In reply to Eli Schwartz from comment #9)
> > As far as I can tell there is zero purpose of -DENABLE_LTO=ON, since all it
> > does is compile a test program to see whether the compiler implements LTO,
> > then update the CMAKE_BUILD_TYPE=Release / RelWithDebInfo flags to utilize
> > LTO.
> 
> Yup, saw that. My idea here was that IF the user has enabled LTO we should
> at least try to play nice with the build system. It seemed odd to me that
> enabling LTO would let cmake's configure phase say that LTO is disabled,
> even if normal emerge runs will not show this.


cmake's configuration phase says loads of things nobody pays any attention to at all.

It is very true that cmake IPO isn't enabled, it just has no bearing on whether the source code is compiled with LTO.


> > While technically this doesn't "matter" for the eclass defaults, translating
> > -flto into ENABLE_LTO would do one of two things:
> > 
> > - cause -flto to be passed multiple times, if you don't then filter-lto
> 
> We want to get rid of filter-lto, and I verified that it does not pass -flto
> multiple times during compilation; it uses exactly the values I set in
> package.env. Either way the output is correct and what one would expect.


I tested this and if I pass -DENABLE_LTO=ON without -flto in *FLAGS, cmake prints: 

-- Using "Ninja" generator and build type "RelWithDebInfo"
-- LTO/IPO is enabled for Release configuration


but no files are compiled with -flto. So, it's not passing anything at all. Heck if I know why.


> I was more confused that it's apparently not possible to use an inline
> expression like -DENABLE_LTO=$(tc-is-lto) - which is used in the tree but
> apparently does not work at all, hence the conditional block.


Because tc-is-lto is an ebuild function, and returns a bash true/false status (an exitcode, specifically) rather than a text string which no build systems other than cmake comprehend. You need to translate a bash truth value to a cmake command line text string.