• src/sbbs3/filterfile.hpp

    From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Saturday, February 07, 2026 20:51:00
    https://gitlab.synchro.net/main/sbbs/-/commit/46130c4fdf1e0e2fea659ca4
    Modified Files:
    src/sbbs3/filterfile.hpp ftpsrvr.cpp mailsrvr.cpp ratelimit.hpp websrvr.cpp
    Log Message:
    Rate limiter data persists across server recycles now (but not stop/starts)

    Hosts listed in ctrl/ipfilter_exempt.cfg are exempt from the rate limiter now.

    More detailed debug rate-limit-reports including who was most recently rate-limited.

    Moved the FTP request rate limiter to exclude pre-auth commands. Co-pilot again blew my (read my) mind and wrote this comment when I moved this code block, using the exact words I was going to type.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Thursday, February 12, 2026 04:25:00
    https://gitlab.synchro.net/main/sbbs/-/commit/a05069c2abe261a3da1fe000
    Modified Files:
    src/sbbs3/filterfile.hpp ftpsrvr.cpp mailsrvr.cpp main.cpp src/sbbs3/scfg/scfgsys.c src/sbbs3/scfgdefs.h scfglib1.c scfgsave.c services.cpp websrvr.cpp
    Log Message:
    Use scfg_t.cache_filter_files value to control interval between file checks

    rather than the semaphore file check interval of each server.

    So changing type from bool (default: true) to uint (default: 5 [seconds]).

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Saturday, February 28, 2026 18:47:00
    https://gitlab.synchro.net/main/sbbs/-/commit/fb6c6aadd2d82c137cbeace2
    Modified Files:
    src/sbbs3/filterfile.hpp
    Log Message:
    Use explicitly initialized pthread_mutex_t instead of std::mutex

    Using a default initializer for the std::mutex didn't resolve the issue
    (crash in MSVCP140.dll).
    Explicitly calling mutex.lock/unlock (rather than using a lock_guard) didn't resolve the issue.

    trashCan constructor now calls filterFile constructor (to get the mutex initialized).

    Resolves issue #1089 (with older MSVC++ runtime libraries)

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Tuesday, March 10, 2026 01:43:00
    https://gitlab.synchro.net/main/sbbs/-/commit/538aceb6f38bbf5d6927f523
    Modified Files:
    src/sbbs3/filterfile.hpp
    Log Message:
    Fix (new) mutex leak by destroying in destructor

    As pointed out by Deuce in code review

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Wednesday, March 11, 2026 00:22:00
    https://gitlab.synchro.net/main/sbbs/-/commit/2fb010d6c3f20027e8b54d2a
    Modified Files:
    src/sbbs3/filterfile.hpp ftpsrvr.cpp mailsrvr.cpp main.cpp services.cpp Log Message:
    Fix thread_down()-vs-cleanup() race causing heap corruption on Windows

    thread_down() must be the very last operation in every server thread,
    because cleanup() proceeds to destroy shared resources (active_clients, sockets, filterFile/trashCan objects) as soon as thread_count drops.
    When a thread called thread_down() but continued accessing shared
    resources afterward, cleanup() could destroy those resources
    concurrently, corrupting the MSVC debug heap and triggering assertions
    in strListFreeStrings during trashCan deletion.

    Move thread_down() after all shared-resource access in:
    - mailsrvr.cpp: smtp_thread, pop3_thread, sendmail_thread
    - ftpsrvr.cpp: ctrl_thread (ftp_client_thread)
    - services.cpp: 6 sites across js/native service threads
    - main.cpp: passthru_socket_thread, events_thread

    Also fix filterfile.hpp Rule of Five: delete copy/move operations
    to prevent latent double-free, and remove unsafe default constructor
    that left the pthread_mutex uninitialized.

    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Wednesday, March 11, 2026 20:52:00
    https://gitlab.synchro.net/main/sbbs/-/commit/c8490b7429f47698e2988f63
    Modified Files:
    src/sbbs3/filterfile.hpp ftpsrvr.cpp mailsrvr.cpp main.cpp services.cpp websrvr.cpp
    Log Message:
    Support and use static instances of filterFile/trashCan objects in TCP servers

    For an unknown reason, deleting dynamically-created instances of these objects could cause MSVC debug-heap assertions under the right/rare circumstance.
    This change is a "punt" as it resolves the issue, but I don't understand why and have run out of ideas, tools, and patience trying to root-cause it.

    See issue #1099 for all the details.

    These classes now default-initialize every member since the default constructor does not and I didn't want to leave uninitized mutexes or strings potentially hanging around (before each ::init() member was called). That change alone did not fix the mysterious issue.

    These classes still support dynamic creation/deletion if/when we ever want to use that pattern again, e.g. to reproduce the mysterious issue (it's backward compatible).

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Wednesday, March 11, 2026 21:48:00
    https://gitlab.synchro.net/main/sbbs/-/commit/9168bd69aaa634d555a248e1
    Modified Files:
    src/sbbs3/filterfile.hpp ftpsrvr.cpp mailsrvr.cpp main.cpp services.cpp websrvr.cpp
    Log Message:
    Create and use trashCan/filterFile reset() method to reset filter stats/times

    This reintroduces the behavior before the previous commit: where we were deleting the filter/trashcan objects upon server termination and the stats
    and timestamps would get reset/re-initialized when the server was initialized.

    I experimented with freeing the list in reset() and the old debug-heap assertions would return (now in the calls to reset() rather than the destructor), so that's more data for issue #1099.

    I don't think we really care about the memory allocated for the lists while
    the server is terminated. It'll get freed if/when the server is restarted
    or the process is terminated and the destructors are called. It's not a leak.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Sunday, March 15, 2026 23:44:00
    https://gitlab.synchro.net/main/sbbs/-/commit/869eaababe5e96efa458b807
    Modified Files:
    src/sbbs3/filterfile.hpp
    Log Message:
    Add a comment about issue #1099 trigger to reset(), no effective change

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Thursday, June 25, 2026 17:54:00
    https://gitlab.synchro.net/main/sbbs/-/commit/d3706726a898cea33d7e7842
    Modified Files:
    src/sbbs3/filterfile.hpp findstr.c findstr.h
    Log Message:
    Fix Win32 debug-heap assertion freeing cached filter lists (GitLab #1099)

    filterFile::listed() (and ~filterFile/reset()) freed its cached str_list
    with strListFree(), which links locally into each server module (xpdev is statically linked), but built it with findstr_list(), which is dllimport'd
    from sbbs.dll. So the list was allocated in sbbs.dll's CRT heap and freed
    in the server module's own /MTd CRT heap. Each statically-linked CRT keeps
    a per-module debug block list, so the freeing module's _free_dbg_nolock
    can't find the block and asserts at debug_heap.cpp:996.

    This is why the issue was debug/Windows-only and undetectable by App
    Verifier: in release the UCRT heap is GetProcessHeap() (shared across
    modules) so the cross-module free actually succeeds, and non-Windows
    shares one libc heap -- only the Win32 debug CRT's per-module accounting notices. It was also reproducible single-threaded (deterministic heap
    mismatch, not a race) and only via findstr_list() (the lone allocation
    crossing the DLL boundary). Affects every filter object (ip_can,
    ip_silent_can, host_can, host_exempt) in every server, since filterFile
    is a header-only class compiled into each module. Present since the class
    was introduced in cd30ec58a (press-5-filled).

    Pair findstr_list() with a findstr_list_free() exported from the same translation unit (sbbs.dll), and call it from all three filterFile free
    sites so allocation and deallocation share one heap. This also lets the strListFree() that #1099 had to comment out of reset() be restored (now
    under the object mutex, since reset() runs in the shutdown path where a
    late client thread may still be in listed()).

    Verified: rebuilt services.dll imports both findstr_list and
    findstr_list_free from sbbs.dll. Built clean on Win32/Release.

    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net