tonumber can return int or float; claiming TY_INT always used CALL_INT and
post-entry declined on float results. Drop it from the int-return claim
so CALL_VAL preserves the typed stack value. EXEC pins "3.5"/"3.0"/"17".
Standalone tests/db never saw LBUF_SIZE (no alloc.h). Define it in the
Makefile to match alloc.h, and include <cstring> in sqlite_backend for
the same TINYMUX_TYPES_DEFINED compile path.
Two sites derived the ".sqlite" sibling of mudconf.indb by open-coding
the same replace-or-append with strcpy/strcat, neither checking the
remaining capacity:
mux/modules/engine/engine_com.cpp (bMinDB RemoveFile) SIZEOF_PATHNAME
mux/modules/engine/attrcache.cpp (cache_init) LBUF_SIZE
input_database is cf_string_dyn with a maximum of SIZEOF_PATHNAME, so its
content can be SIZEOF_PATHNAME-1 characters. ".sqlite" is seven more plus
a terminator, so the engine_com buffer overflows in both branches: the
append writes eight bytes past a full buffer, and even the ".db" branch
that replaces three characters with seven needs four bytes it may not
have. attrcache was safe only because indb is capped well below
LBUF_SIZE -- an implicit cross-module invariant, not a check.
Replaced with one bounds-checked derive_sqlite_path() in sqlite_backend,
which both TUs already include. It returns false and leaves the buffer
untouched when the result will not fit; callers log and take a defined
path -- cache_init returns HF_OPEN_STATUS_ERROR after releasing the
backend it had just allocated, and the bMinDB case skips the removal,
since a path that cannot be formed names no file to remove. No new
abort: the failure is reported, not asserted.
Behaviour is unchanged for every input that fits, including the edge
cases the old `n > 3` test produced -- ".db" appends rather than
replaces, "a.dbx" and "db" append -- so this is not a silent change of
the derived name.
Verified with ASan on a standalone harness carrying both versions, an
input_database at exactly the configured maximum:
old, no .db suffix heap-buffer-overflow, WRITE of size 8
old, .db suffix heap-buffer-overflow, WRITE of size 8
new, both returns false, no diagnostic
and the normal cases: netmux.db -> netmux.sqlite, netmux ->
netmux.sqlite, data/netmux.db -> data/netmux.sqlite, plus the exact-fit
boundary accepted and one byte over rejected.
smoke 1489/1489 on both routes, 0 crashes, 314/314 dispatched.
Closes#1411.
Story 2a: Extend IStorageBackend with Count(), GetModCount(), and
GetAllModCounts(). Implement in CSQLiteBackend. Route all attribute
access in attrcache.cpp and db.cpp through the interface, eliminating
GetDB() bypasses for attribute-specific operations.
Story 2b: New docs/attribute-metadata.md defines the per-attribute
metadata contract: (object, attrnum) -> (value, owner, flags, mod_count),
encoding requirements, mod_count semantics, and iteration guarantees.
Story 2c: Confirmed @search never queries the attributes table directly.
SQL fast-paths use object metadata only; eval= predicates go through
cache_get() -> IStorageBackend. No changes needed for backend swap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>