Skip to content

3D: add entrypoint wrappers that check the whole input was consumed - #314

Open
tahina-pro wants to merge 2 commits into
project-everest:masterfrom
tahina-pro:_taramana_3d_wrapper_consumes_all
Open

3D: add entrypoint wrappers that check the whole input was consumed#314
tahina-pro wants to merge 2 commits into
project-everest:masterfrom
tahina-pro:_taramana_3d_wrapper_consumes_all

Conversation

@tahina-pro

Copy link
Copy Markdown
Member

Fixes #312.

Problem

For a type marked entrypoint, the 3d frontend generates a wrapper
ModuleCheckTyp in ModuleWrapper.c/.h. That wrapper calls the verified
validator and only checks whether it reported an error:

uint64_t ep_status = ModuleValidateTyp(/* ... */, base, len, 0);
if (EverParseIsError(ep_status)) { /* report */ return FALSE; }
return TRUE;

On success, ep_status carries the position up to which the input was
consumed, but the wrapper discards it. So a buffer that starts with a valid
message and then contains extra bytes is accepted, with no way for the caller
to notice from the wrapper's return value alone.

Solution

Next to every existing wrapper, 3d now generates a Complete variant with the
same signature, which additionally checks that the validator consumed the whole
input buffer.

existing new
ModuleCheckTyp ModuleCheckCompleteTyp
ModuleProbeFnCheckTyp ModuleProbeFnCheckCompleteTyp
user-provided entrypoint(Foo) FooComplete

For example, for

entrypoint
typedef struct _POINT { UINT32 x; UINT32 y; } POINT;

TWrapper.h now declares:

BOOLEAN TCheckPoint(uint8_t *base, uint32_t len);
BOOLEAN TCheckCompletePoint(uint8_t *base, uint32_t len);

and the body of the new wrapper is the old one plus:

if (EverParseGetValidatorErrorPos(ep_status) != (uint64_t)len)
{
	TEverParseError("_POINT", "", "unexpected trailing bytes");
	return FALSE;
}

Callers therefore choose explicitly: ModuleCheckTyp when the input buffer may
legitimately contain trailing data (e.g. a fixed-size receive buffer), and
ModuleCheckCompleteTyp when the buffer is meant to hold exactly one message.

Naming

Complete echoes LowParse's parse_consumes_all / "complete parser"
vocabulary, keeps the Module...Check...Typ shape so both variants sort
together in the header, and extends to user-provided entrypoint names as a
plain suffix. CheckAll was rejected because CheckAll is already used as a
custom entrypoint name in the documentation.

Design notes

  • Buffer binding only. The Complete wrappers are generated only for the
    default buffer input stream binding. With the other bindings the wrapper
    returns parsedSize over an unbounded stream, so there is neither a known
    total length to compare against nor a return convention to signal "trailing
    bytes". Extending this to streams would be a separate change.

  • Error reporting. The error frame is not filled in this case (the
    validator itself succeeded), so the wrapper calls ModuleEverParseError
    directly with the type name, an empty field name, and
    "unexpected trailing bytes".

  • No new locals. The check reuses ep_status and len, so the
    --hoist_locals, --init_locals and --goto_for_early_return variants of
    the generated code are unaffected structurally; under --goto_for_early_return
    the check does goto exit like the other failure paths.

  • Name collisions. Since FooComplete is now derived from entrypoint(Foo),
    3d rejects a .3d file declaring two entrypoints whose public names collide,
    including when one is the Complete counterpart of the other:

    ./Dup.3d:(4,16): (Error) Duplicate entrypoint name FooComplete. Note that 3d
    reserves the name of every entrypoint suffixed with `Complete' for the wrapper
    that additionally checks that the whole input was consumed.
    
  • Visibility. The new wrappers follow exactly the same rules as the
    existing ones: public in the header iff a plain entrypoint is declared, and
    emitted static in the .c file when only entrypoint probe attributes are
    present. Complete probe wrappers call the Complete main wrapper.

Changes

Commit 1 — 3d: generate wrappers that check the whole input was consumed

  • src/3d/Target.fst, src/3d/Target.fsti: new wrapper_complete_name,
    probe_wrapper_complete_name and complete_name_of_custom_name;
    wrapped_call_buffer gained a check_complete flag;
    print_validators_for_one_decl emits both families; new
    entrypoint-name-collision check.
  • src/3d/tests/check_complete/: new test (see below), wired into
    src/3d/tests/Makefile.
  • src/3d/tests/goto_return/snapshot/: regenerated.
  • doc/3d-lang.rst, doc/3d.rst: documentation.

Commit 2 — doc: regenerate 3d-snapshot for the new 'Complete' wrappers

  • doc/3d-snapshot/*Wrapper.{c,h}: mechanical output of
    make 3d-doc-snapshot, separated out to keep the first commit reviewable.

Testing

New test src/3d/tests/check_complete/, run as part of the 3d test suite. Its
driver checks that:

  1. on an exact-size input, both the plain and the Complete wrapper accept;
  2. on an input with trailing bytes, the plain wrapper accepts while the
    Complete wrapper rejects and reports exactly one error;
  3. on a too-short input, both reject;
  4. a named entrypoint's Complete counterpart (CheckHeaderComplete) behaves
    the same way.

make -j16 -k 3d-test passes (exit code 0), including the regenerated
snapshots and the existing hash-check tests.

tahina-pro and others added 2 commits August 19, 2026 20:53
Fixes project-everest#312. The existing `ModuleCheckTyp` wrappers only check that
validation succeeded, so an input buffer with trailing bytes beyond the
message is silently accepted.

Next to each existing wrapper, 3d now generates a `Complete` variant
with the same signature, which additionally checks that the validator
consumed the whole input buffer:

* `ModuleCheckTyp`               -> `ModuleCheckCompleteTyp`
* `ModuleProbeFnCheckTyp`        -> `ModuleProbeFnCheckCompleteTyp`
* user-provided `entrypoint(Foo)` -> `FooComplete`

On trailing bytes, the wrapper calls `ModuleEverParseError` with the
type name, an empty field name and "unexpected trailing bytes", then
returns FALSE.

Since the wrapper needs to know the length of the whole input, the
`Complete` wrappers are only generated for the `buffer` input stream
binding. 3d now also rejects colliding entry point names, which can
now happen when a user-provided name is the `Complete` counterpart of
another one.

The doc/3d-snapshot snapshots are regenerated in a follow-up commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mechanical output of `make 3d-doc-snapshot' after the previous commit:
every entrypoint in the documentation examples now additionally gets a
`Complete' wrapper, which checks that the validator consumed the whole
input buffer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tahina-pro
tahina-pro requested a review from nikswamy August 20, 2026 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated Check wrapper accepts trailing bytes

1 participant