REF-036 - Adopt GSL contracts for buffer and pointer boundary hardening¶
Summary¶
Introduce the Guidelines Support Library (GSL) only where it makes current buffer, pointer, and size contracts explicit at risky boundaries.
This is not a general style migration. The useful part of GSL for this codebase
is narrow: gsl::span for pointer-plus-length byte ranges, gsl::not_null for
required pointers at API boundaries, and gsl::narrow where lossy numeric
conversion would otherwise be silent.
Current State¶
The app still has parser and persistence surfaces where raw pointers, raw byte arrays, and implicit size contracts are easy to misuse:
srchybrid/SafeFile.cpp- hash and binary read/write helpers such as
ReadHash16(uchar*)andWriteHash16(const uchar*). srchybrid/PartFile.cpp- raw
BYTE*/uchar*buffers,new BYTE[], hash buffers, andWriteToBuffer(...)style helpers. srchybrid/ArchiveRecovery.handsrchybrid/ArchiveRecovery.cpp- packed binary structures, owned byte buffers, and helpers such as
calcUInt16(BYTE*)/calcUInt32(BYTE*). - Newer REST/test helper seams
- route helpers and DTO boundaries where typed contract helpers can prevent future raw pointer or size drift.
These surfaces are release-relevant because mistakes tend to become corrupted metadata, truncated binary parsing, invalid archive recovery, or silent release failures rather than obvious compile errors.
Intended Scope¶
- Add GSL as a setup-managed dependency only with a first real conversion.
- Start with one small, tested boundary where the contract is already obvious:
- fixed-size hash buffers in SafeFile/PartFile helpers, or
- archive recovery byte readers, or
- a REST/test helper boundary that already has strong regression coverage.
- Use
gsl::span<const std::byte>or a project-appropriate byte type when the function requires both pointer and length. - Use fixed-extent spans where the contract is truly fixed-size, for example a 16-byte hash.
- Use
gsl::not_null<T*>only on boundaries that already require non-null and where call sites can satisfy that contract clearly. - Use
gsl::narrowonly in checked paths where rejecting or asserting lossy conversion is the intended behavior.
Out of Scope¶
- Sweeping every pointer in the codebase.
- Replacing MFC containers or UI APIs with GSL wrappers.
- Introducing GSL into hot protocol paths without targeted tests.
- Converting broad call graphs just because a leaf helper was improved.
- Using GSL as a substitute for real parser validation or bounds checks.
Implementation Notes¶
- Pick a boundary that already has tests or where tests can be added in the same slice.
- Avoid ABI churn on old MFC message handlers and UI callbacks.
- Prefer wrapper overloads at boundaries over invasive signature changes across many files.
- Keep old raw-pointer overloads temporarily if they materially reduce risk, but funnel them into the span-based implementation.
- Where GSL makes a latent contract violation visible, treat that as a separate bug or regression-fix commit unless it is inseparable from the conversion.
Acceptance Criteria¶
- GSL is tracked as a setup-managed dependency and is discoverable through the generated workspace dependency contract.
- The first conversion replaces a real raw pointer/size or fixed-buffer contract with an explicit GSL contract.
- The converted code keeps current runtime behavior for valid inputs.
- Invalid size/null/lossy-conversion cases fail deliberately rather than silently.
- The conversion has targeted test coverage for the touched parser, persistence, or helper boundary.
Test Plan¶
- Run setup validation after adding the dependency.
- Build the app and native tests through the supported
emulebb-buildentrypoint. - For SafeFile/PartFile changes, run part-file persistence, known-file, and hash read/write tests.
- For archive recovery changes, run archive recovery and malformed-input tests before expanding scope.
- For REST helper changes, run native web API tests plus the live REST smoke if the route contract changes.
- Include at least one negative or edge-size test when the conversion changes how invalid input is rejected.
Release Value¶
This is useful after WIL or alongside a small parser-hardening slice. It is not a release blocker by itself. The value comes from reducing silent buffer and conversion mistakes in the exact areas where broadband release regressions would be expensive to diagnose later.