Test cases demonstrating functionality (if applicable):
With stats.mod
Before:
[11:08:51] [!s] WHOIS BotA
[11:08:57] [s->] WHOIS BotA
[11:08:57] [@] :irc.example.org 311 BotA BotA eggdrop Clk-8553FB0D * :A deranged product of evil coders
[11:08:57] [@] :irc.example.org 379 BotA BotA :is using modes +ix
[11:08:57] [@] :irc.example.org 378 BotA BotA :is connecting from *@localhost 127.0.0.1
[11:08:57] [@] :irc.example.org 312 BotA BotA irc.example.org :ExampleNET Server
[11:08:57] [@] :irc.example.org 317 BotA BotA 8 1718096929 :seconds idle, signon time
[11:08:57] [@] :irc.example.org 318 BotA BotA :End of /WHOIS list.
[11:08:59] [s->] JOIN #test
[11:08:59] [@] :BotA!eggdrop@Clk-8553FB0D JOIN #test * :A deranged product of evil coders
[11:08:59] BotA joined #test.
[11:08:59] [!m] MODE #test +b
[11:08:59] [m->] MODE #test +b
[11:08:59] [!m] MODE #test +e
[11:08:59] [m->] MODE #test +e
[11:08:59] [!m] MODE #test +I
[11:08:59] [!m] MODE #test
[11:08:59] [!m] WHO #test c%chnufat,222
[11:08:59] [@] :irc.example.org MODE #test +nt
[11:08:59] #test: mode change '+nt ' by irc.example.org
[11:08:59] [@] :irc.example.org 353 BotA = #test :@BotA
[11:08:59] [@] :irc.example.org 366 BotA #test :End of /NAMES list.
[11:08:59] [@] :irc.example.org 368 BotA #test :End of Channel Ban List
[11:08:59] [@] :irc.example.org 349 BotA #test :End of Channel Exception List
[11:09:01] [m->] MODE #test +I
[11:09:01] [@] :irc.example.org 347 BotA #test :End of Channel Invite List
[11:09:02] net: connect! sock 6
[11:09:02] 127.0.0.1: GET /test/onchan/ HTTP/1.1
[11:09:02] 127.0.0.1: Host: 127.0.0.1:8033
[11:09:02] 127.0.0.1: User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0
[11:09:02] 127.0.0.1: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
[11:09:02] 127.0.0.1: Accept-Language: en-US,en;q=0.5
[11:09:02] adding accepted language: 'en'
[11:09:02] adding accepted language: 'en'
[11:09:02] 127.0.0.1: Accept-Encoding: gzip, deflate, br, zstd
[11:09:02] 127.0.0.1: Referer: http://127.0.0.1:8033/test/
[11:09:02] 127.0.0.1: Connection: keep-alive
[11:09:02] 127.0.0.1: Upgrade-Insecure-Requests: 1
[11:09:02] 127.0.0.1: Sec-Fetch-Dest: document
[11:09:02] 127.0.0.1: Sec-Fetch-Mode: navigate
[11:09:02] 127.0.0.1: Sec-Fetch-Site: same-origin
[11:09:02] 127.0.0.1: Sec-Fetch-User: ?1
[11:09:02] 127.0.0.1: Priority: u=1
[11:09:02] 127.0.0.1: Pragma: no-cache
[11:09:02] 127.0.0.1: Cache-Control: no-cache
[11:09:02] 127.0.0.1:
[11:09:02] now sending...
=================================================================
==2274420==ERROR: AddressSanitizer: heap-use-after-free on address 0x515000002a10 at pc 0x7e28589765fe bp 0x7ffe6e7b1880 sp 0x7ffe6e7b1870
READ of size 8 at 0x515000002a10 thread T0
#0 0x7e28589765fd in template_send_usermode core/templates_stats_commands.c:1000
#1 0x7e2858966d96 in templates_content_send core/templates_content.c:251
#2 0x7e2858976406 in template_send_onchanlist core/templates_stats_commands.c:992
#3 0x7e2858966d96 in templates_content_send core/templates_content.c:251
#4 0x7e285896b5c0 in template_send core/templates.c:165
#5 0x7e2858980829 in process_get_request core/http_processing.c:327
#6 0x7e285893cd66 in http_activity core/mini_httpd.c:390
#7 0x6095dd728adc in mainloop main.c:797
#8 0x6095dd72ce37 in main main.c:1213
#9 0x7e285d239c87 (/usr/lib/libc.so.6+0x25c87) (BuildId: 32a656aa5562eece8c59a585f5eacd6cf5e2307b)
#10 0x7e285d239d4b in __libc_start_main (/usr/lib/libc.so.6+0x25d4b) (BuildId: 32a656aa5562eece8c59a585f5eacd6cf5e2307b)
#11 0x6095dd63f4a4 in _start (/home/michael/eggdrop/eggdrop-1.9.5+0x2774a4) (BuildId: 61a3415c7de8a5b01975ffb1e0922903878400f5)
0x515000002a10 is located 400 bytes inside of 456-byte region [0x515000002880,0x515000002a48)
freed by thread T0 here:
#0 0x7e285e4fb422 in free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:52
#1 0x6095dd731b17 in n_free /home/michael/projects/eggdrop/src/mem.c:443
#2 0x6095dd74774f in mod_free /home/michael/projects/eggdrop/src/modules.c:1033
#3 0x7e2859941d33 in init_channel .././channels.mod/tclchan.c:2006
#4 0x7e285994334f in clear_channel .././channels.mod/tclchan.c:2085
#5 0x7e28593d95bd in reset_chan_info .././irc.mod/irc.c:423
#6 0x7e28593588f0 in gotjoin .././irc.mod/chan.c:2120
#7 0x7e2859712fee in server_raw .././server.mod/server.c:1410
#8 0x6095dd769bfa in tcl_call_stringproc_cd /home/michael/projects/eggdrop/src/tcl.c:332
#9 0x7e285e27e7ff in TclNRRunCallbacks /usr/src/debug/tcl/tcl8.6.14/generic/tclBasic.c:4539
previously allocated by thread T0 here:
#0 0x7e285e4fca31 in malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x6095dd7304ea in n_malloc /home/michael/projects/eggdrop/src/mem.c:342
#2 0x6095dd7472b6 in mod_malloc /home/michael/projects/eggdrop/src/modules.c:1005
#3 0x7e2859941e45 in init_channel .././channels.mod/tclchan.c:2009
#4 0x7e285994478f in tcl_channel_add .././channels.mod/tclchan.c:2154
#5 0x7e285992f09d in tcl_channel .././channels.mod/tclchan.c:1253
#6 0x6095dd769bfa in tcl_call_stringproc_cd /home/michael/projects/eggdrop/src/tcl.c:332
#7 0x6095dd769d1b in tcl_call_stringproc /home/michael/projects/eggdrop/src/tcl.c:341
#8 0x7e285e27e7ff in TclNRRunCallbacks /usr/src/debug/tcl/tcl8.6.14/generic/tclBasic.c:4539
SUMMARY: AddressSanitizer: heap-use-after-free core/templates_stats_commands.c:1000 in template_send_usermode
Shadow bytes around the buggy address:
0x515000002780: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
0x515000002800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x515000002880: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x515000002900: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x515000002980: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x515000002a00: fd fd[fd]fd fd fd fd fd fd fa fa fa fa fa fa fa
0x515000002a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x515000002b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x515000002b80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x515000002c00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x515000002c80: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa 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
==2274420==ABORTING
Found by: michaelortmann Patch by: michaelortmann Fixes:
One-line summary: Add HOOK for reset (channel) member
Additional description (if needed):
stats.mod
caches the result ofismember()
up to 1 minute and suffers a heap-use-after-free when eggdrop frees members during a channel reset here: https://github.com/eggheads/eggdrop/blob/d58f56347a16947757a0112715b13561f49fb539/src/mod/channels.mod/tclchan.c#L2006C9-L2006C10With this PR combined with https://github.com/michaelortmann/stats.mod/commit/02ff9916b33e2e5d879a1d09989a16dffa6fb6ed
stats.mod
will add a new hook thatchannel.mod
will trigger. whenchannel.mod
resets channel members it will call that hook andstats.mod
will resync.Test cases demonstrating functionality (if applicable): With
stats.mod
Before:After: