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 }
(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
GCC 10 works, 11 doesn't, bisecting.
Started with r11-5391-gbb07490abba850
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 ```
``` /** * 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)
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!
*** Bug 754021 has been marked as a duplicate of this bug. ***
(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.
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
(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.
(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.
FWIW Sam's patch has been backported to 4.4.x with the just-released 4.4.6. Giving that a try and then a PR.
The bug has been closed via the following commit(s): https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=271d5459ef4a511414f2f968afe4c728fa5664b7 commit 271d5459ef4a511414f2f968afe4c728fa5664b7 Author: Holger Hoffstätte <holger@applied-asynchrony.com> AuthorDate: 2025-04-17 09:53:02 +0000 Commit: Sam James <sam@gentoo.org> CommitDate: 2025-04-17 13:02:35 +0000 net-analyzer/wireshark: add 4.4.6, drop py3.10, allow LTO again Bug: https://bugs.gentoo.org/754021 Closes: https://bugs.gentoo.org/941890 Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com> Signed-off-by: Sam James <sam@gentoo.org> net-analyzer/wireshark/Manifest | 2 + net-analyzer/wireshark/wireshark-4.4.6.ebuild | 328 ++++++++++++++++++++++++++ 2 files changed, 330 insertions(+) Additionally, it has been referenced in the following commit(s): https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=71a7e7c01404fe221bb9763dd3d4f58f5a701984 commit 71a7e7c01404fe221bb9763dd3d4f58f5a701984 Author: Holger Hoffstätte <holger@applied-asynchrony.com> AuthorDate: 2025-04-17 09:59:39 +0000 Commit: Sam James <sam@gentoo.org> CommitDate: 2025-04-17 13:02:45 +0000 net-analyzer/wireshark: drop py3.10, allow LTO again in -9999 Bug: https://bugs.gentoo.org/754021 Bug: https://bugs.gentoo.org/941890 Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com> Closes: https://github.com/gentoo/gentoo/pull/41627 Signed-off-by: Sam James <sam@gentoo.org> net-analyzer/wireshark/wireshark-9999.ebuild | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
The bug has been referenced in the following commit(s): https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=fad8ff8a45afc83559f8df695cf96dfec51d3e8a commit fad8ff8a45afc83559f8df695cf96dfec51d3e8a Author: Sam James <sam@gentoo.org> AuthorDate: 2025-04-18 04:21:42 +0000 Commit: Sam James <sam@gentoo.org> CommitDate: 2025-04-18 04:23:01 +0000 net-analyzer/wireshark: fix runtime with LTO Qt's qcompilerdetection.h currently checks for whether -fPIE is being used along with QT_USE_PROTECTED_VISIBILITY ("reduce relocations", which Qt automatically uses if supported). It bails out if -fPIE is used, as -fPIC is required instead. If LTO is used, when one does something like: (1) g++ -c -flto -fPIC qtlto.cc (2) g++ -pie -fPIE qtlto.o -o qtlto At point (1), the Qt check in the headers fires, and everything is fine, because we're indeed using -fPIC, and GCC doesn't automatically add -fPIE when built with --enable-default-pie if -fPIC is present on the command line. GCC may apply optimisations at this point given Qt is using -mno-direct-extern-access and it was built with -fPIC not -fPIE. Later, at point (2), -fPIE is passed. This happens in Wireshark because `CMAKE_POSITION_INDEPENDENT_CODE` gets set in CMakeLists.txt. With LTO, there's no opportunity for the Qt sanity check in headers to fire again, as everything is already long-preprocessed and GCC will have applied some optimisations already assuming the -fPIC code model in (1). But as slyfox says at https://bugs.gentoo.org/754021#c12, GCC merges -fPIC -fPIE to -fPIE at LTO-time (-fPIC coming from the earlier LTO object in (1), and -fPIE was just-passed on the command line). qtlto (or Wireshark) then crashes. For Wireshark, this looks like: ``` #0 0x00007ff40e529cf0 in QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::get (this=<optimized out>) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/tools/qscopedpointer.h:112 #1 qGetPtrHelper<QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> > > (ptr=<optimized out>) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/global/qtclasshelpermacros.h:128 #2 QObject::d_func (this=<optimized out>) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/kernel/qobject.h:108 #3 QObjectPrivate::get (o=<optimized out>) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/kernel/qobject_p.h:150 #4 doActivate<false> (sender=0x0, signal_index=9, argv=argv@entry=0x7ffe59a73c30) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/kernel/qobject.cpp:4003 #5 0x00007ff40e4d2809 in QMetaObject::activate (sender=<optimized out>, m=m@entry=0x7ff40f44f6c0 <QGuiApplication::staticMetaObject>, local_signal_index=local_signal_index@entry=1, argv=argv@entry=0x7ffe59a73c30) at /usr/src/debug/dev-qt/qtbase-6.8.3/qtbase-everywhere-src-6.8.3/src/corelib/kernel/qobject.cpp:4183 #6 0x00007ff40ead5676 in QGuiApplication::screenAdded (this=<optimized out>, _t1=<optimized out>) [...] ``` We need to drop -fPIE somehow at link-time accordingly. There's a few ways of doing this but I've gone for not calling `check_pie_supported()` (see (7) below). (Analysis on fixing this in other packages may depend on whether any static libraries *installed* by CMake where -fPIC was no longer passed for those, we would have a problem. I'd tried to use POSITION_INDEPENDENT_CODE at first but then -fPIC gets dropped as well everywhere, and setting the target property to false for just the Wireshark executable also doesn't work because it'll pass -no-pie which isn't what we want.) There are some questions: (3) Why doesn't this happen with Clang, given that Clang has -fno-direct-access-external-data (equivalent to GCC's -mno-direct-extern-access), even when Qt is built with bfd (not lld)? The answer seems to be that Clang doesn't implement the optimisation yet to avoid copy-relocations where possible. GCC implemented that in 5.x in r5-5573-g77ad54d911dd7c. (4) Why doesn't this (seem to) happen in other distributions? nextcloud-client suffers from the same issue analysed here, see https://bugs.gentoo.org/933110. The upstream bug at https://github.com/nextcloud/desktop/issues/2790 was reported by a Debian developer (cgzones), so it's a reasonable assumption that it can happen on Debian. Debian is one of few distributions (we're another) to use --enable-default-pie in GCC rather than just passing it to all package builds in the package manager: it's possible that some distros are just disabling -fPIE or adding a workaround like we did for https://bugs.gentoo.org/552440. Not many distros build with LTO either. Debian also stopped building Wireshark with LTO because of a bug in Wireshark itself (https://bugs.gentoo.org/941890), so I guess they disabled LTO and didn't notice this crash. (This is enough for me to be more confident in my analysis, anyway.) (5) Could Qt communicate this somehow automatically? I think it might be able to if statically linking Qt and Qt was built with LTO. Otherwise, I think the only option would be an ELF .note. pkg-config could maybe work but you can't assume all Qt consumers use that... See the discussion around <https://bugreports.qt.io/browse/QTBUG-45755?focusedId=282483&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-282483>: > Thiago Macieira added a comment - 22 May '15 17:11 > There aren't that many autoconf-based Qt5 builds and we've never exported the flag anyway. (It might be worth bringing this .note idea up to Thiago and/or H.J. but I'm not sure yet if it'll work.) On the Qt side, -fPIC gets passed in to various places before because Qt's CMake config files have INTERFACE_COMPILE_OPTIONS w/ -fPIC. Maybe the answer is for Qt packages to never use CMAKE_POSITION_INDEPENDENT_CODE instead. This came up in https://gitlab.kitware.com/cmake/cmake/-/issues/15570. (6) Could we just disable "reduce relocations" in Qt itself, given that the workaround here will need to be applied in various Qt consumers? This would significantly impact startup times of applications using Qt and there don't seem to be too many applications doing this (only 2 known so far in Gentoo: Wireshark and nextcloud-client). (7) Is the mechanism used to fix this brittle? Yes, we're relying on a CMake bug/feature for now at https://gitlab.kitware.com/cmake/cmake/-/issues/25588 so it doesn't try to enable *or* disable PIE at link-time and we can just rely on our toolchain defaults. Thanks to Arusekk for producing a minimal example and reporting it upstream to Wireshark, thanks to slyfox for analysing the interaction with LTO, thanks to Holger for the discussion around it and testing, and thanks to Eli for reviewing the commit message. Bug: https://bugs.gentoo.org/552440 Bug: https://bugs.gentoo.org/754021 Bug: https://bugs.gentoo.org/933110 Bug: https://bugs.gentoo.org/941890 Bug: https://gitlab.kitware.com/cmake/cmake/-/issues/15570 Bug: https://gitlab.kitware.com/cmake/cmake/-/issues/25588 Bug: https://gitlab.kitware.com/cmake/cmake/-/issues/23980 Bug: https://gitlab.com/wireshark/wireshark/-/issues/17040 Bug: https://bugreports.qt.io/browse/QTBUG-45755 Bug: https://bugreports.qt.io/browse/QTBUG-47942 Bug: https://gcc.gnu.org/PR65248 Bug: https://gcc.gnu.org/PR65886 Thanks-to: Arusekk <arek_koz@o2.pl> Thanks-to: Sergei Trofimovich <slyfox@gentoo.org> Thanks-to: Holger Hoffstätte <holger@applied-asynchrony.com> Thanks-to: Eli Schwartz <eschwartz@gentoo.org> Signed-off-by: Sam James <sam@gentoo.org> net-analyzer/wireshark/files/4.4.6-lto.patch | 164 +++++++++++++++++++++ ...hark-4.4.6.ebuild => wireshark-4.4.6-r1.ebuild} | 11 +- net-analyzer/wireshark/wireshark-9999.ebuild | 11 +- 3 files changed, 175 insertions(+), 11 deletions(-)