Skip to content

[Medium] Structured-candidate JSON codec has a marker-collision bug and allows non-finite floats #38

Description

@gratus907

Severity: Medium

The structured-candidate JSON codec has a marker-collision vulnerability that silently corrupts candidates, and non-finite floats can enter "JSON-safe" checkpoint payloads despite the documented contract.

1. Marker collision silently corrupts legitimate candidatessrc/variopt/spaces/serialization.py:30-45, 72-84

A legitimate mapping-shaped candidate whose key happens to equal an internal marker round-trips to the wrong type:

space_candidate_to_dict({"__variopt_bytes__": "abcd"})
# decodes back as b'\xab\xcd', not the original mapping

Any structured space with such a key silently corrupts every checkpoint containing it — this surfaces later as a confusing space.validate failure, or (if the space happens to accept bytes) silent state corruption with no error at all.

Relatedly, space_candidate_to_dict's docstring promises a TypeError for unsupported values, but no such branch exists — unsupported leftovers (e.g. a stray list or None) pass through silently and only fail later at decode or at json.dumps.

2. Non-finite floats pass validation and break the JSON-safe contractsrc/variopt/algorithms/population/csa/banking/growth/state.py:36, banking/bank.py:85-94

CSABankGrowthPolicy(initial_energy_gap_limit=float("inf")) passes validation (only < 0 is rejected) and serializes as the non-standard JSON token Infinity, contradicting the checkpoint docs' "JSON-safe" claim. Conversely, BankEntry.from_dict accepts inf/NaN (and True) for value with no finiteness gate on decode.

Fix direction

Escape marker-shaped keys on encode (e.g. wrap plain mappings whose key set matches a marker), or reject them with a clear TypeError; add the missing else-branch TypeError for unsupported leftovers. Validate finiteness at the growth-policy to_dict/from_dict boundary, or explicitly require/document json.dumps(..., allow_nan=False) for checkpoint writers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions