DASM v2.20.16
DASM 2.20.16 — Code Quality & Safety Release
This release doesn't add features or change assembler behaviour on valid input. Instead, it represents the most thorough review and repair of the DASM codebase in many years — a ground-up pass through every source file to address accumulated technical debt, fix latent bugs, and bring the code up to modern C standards.
Code review and bug fixes
A systematic audit of all C source files identified 19 distinct bugs ranging from critical memory safety issues to broken macros. Every one of them was fixed.
The most serious were memory safety bugs: a heap buffer overflow in asmerr() when deeply chained forward references drive the assembler past its pass limit; stack buffer overflows in the source line parser, the listing output formatter, and the comment cleanup path; and a use-after-free in the small block allocator where small_free_all() freed memory that the allocator's internal cursor still pointed at, so the next allocation would write into freed heap. These bugs were silent on normal input but would trigger on crafted or pathological source files.
Other fixes addressed correctness issues: a memset with the wrong byte count leaving the pass-stop array partially uninitialised; undefined behaviour from signed left-shifts in the expression evaluator and the DS directive; a divide-by-zero in ALIGN when given a zero argument; a per-pass memory leak in INCBIN; a buffer overflow risk in DC when generating multi-byte character constants; non-literal format strings passed to fprintf; and getc() return values stored as unsigned, which masks the EOF sentinel and produces garbage on truncated binary input. ftohex was also opening its input in text mode, which mangles 0x1A and line endings on Windows.
Several header issues were fixed: asm.h was missing its include guard entirely, causing duplicate enum declarations whenever two headers included it; symbols.h had drifted into an aspirational redesign that didn't correspond to the actual symbols.c implementation and was rewritten from scratch; panic() existed only as a forward declaration inside main.c, making it invisible to other translation units; and errors.c was missing its util.h include, making getprogname() and strlcat() invisible to it on Linux, producing implicit-declaration warnings.
The broken DASM_PRINT_LEGAL macro in version.h — which expanded to syntactically invalid C and would cause a compile error if invoked — was also corrected.
Build system
Object files are now placed in src/.objs/<OS>/ rather than directly in src/. This allows macOS and Linux builds to coexist in the same source tree without .o files from one platform overwriting the other — a practical issue when developing with a shared or mounted directory.
Testing
The full test suite (96/97 passing, with the one pre-existing failure unchanged) was run under AddressSanitizer and UndefinedBehaviourSanitizer. The sanitiser run exposed the asmerr() heap overflow, which was not detectable by code review alone. No other sanitiser errors were found across normal and adversarial test inputs.
Style
All source files were reformatted to consistent K&R style using clang-format. This is a whitespace-only change with no effect on compiled output, but makes the code significantly easier to read and diff going forward.