Subj : SMB data refcounts: per-field free leaks the tail's spill block, and a To : GitLab note in main/sbbs From : Rob Swindell Date : Tue Sep 29 2026 17:09:40 https://gitlab.synchro.net/main/sbbs/-/work_items/1253#note_10502 ## A third trigger: fixsmb `fixsmb` rebuilds the `.sda` from zero, then re-references each live header with the per-field `smb_incmsg_dfields()`. That never counts a tail's spill block, so after a `fixsmb` every live message whose tail spills has that block counted 0. No sharing and no pack are needed. The next self-packing allocation can then take the block and overwrite the end of the tail (the signature). A fourth reproducer case shows it: one message with a 200-byte body and a 100-byte tail goes from `block0=1 block1=1` to `block0=2 block1=0` after the rebuild, and a new 100-byte message is then written into block 1, over 48 of the tail's 100 bytes. The shipped `fixsmb` binary gives the same `2 0` on the same base. `chksmb` could not see this: it checked each field's blocks with the same per-field arithmetic. ## What one production system had A read-only scan of every `.sda` against its headers: * **Mail base (self-packing):** 2 blocks still in use were counted 0, both spill blocks: one of a 10-header shared message (outbound mail to 6 recipients plus 4 bounces, counts `10 20 0`) and one of a single message (`1 2 0`). No other message had been written into them yet. The counts match a per-field rebuild exactly, but the cause is not known: the only record of `fixsmb` being run on that base predates both messages. * **Four fast-allocation sub-boards:** 693 spill blocks still in use were counted 0, and every block with a count of 2 was the per-field double count of an unshared message: the signature of a past `fixsmb`. Harmless there, since fast allocation never reuses a freed block. * **File bases and hyper-allocated sub-boards:** not affected. ## The fix Fixed by ff8e663eac (have-11-shield, 2026-09-29). `smb_freemsg_dfields()` and `smb_incmsg_dfields()` now adjust the counts over the message's whole data span in one call, matching allocation and `smbutil pack`, and `fixsmb` rebuilds the same way. `smb_getmsgdatlen()` returns the data's extent (the end of the furthest field), which equals the old sum for all existing data. `chksmb` now checks every block of the span, so it reports these blocks as misallocated active data blocks; `fixsmb` repairs them. On data already on disk: single-copy data was always counted by span (that is how it is allocated), and packed data was counted by span by the pack, so both are counted correctly from now on. Shared mail with a spilling tail that has never been packed is counted per field; deleting one copy of it can free its spill block early. Only messages that already exist at upgrade are affected, and `chksmb` followed by `fixsmb` removes the exposure. Verified on full copies of the bases above: the old `chksmb` passed the mail base; the new one reported the 11 headers, and after the new `fixsmb` every block's count equals the number of headers whose data spans it. The same `fixsmb` has since been run on the live mail base with the same result. New tests: `src/smblib/tests/smballoctest.c` (all four cases failed before the change and pass after it). -- *Authored by Claude (Claude Code), on behalf of @rswindell* --- SBBSecho 3.37-Linux * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705) .