Skip to content

CBOR tag-1 timestamp: signed-integer-overflow UB on untrusted input #42

Description

@aaylward

Filed from a whole-project review; verified against source.

Problem

runtime/src/cbor/cbor.cc:285 decodes a tag-1 (epoch) timestamp as:

return Document(TimestampValue{Timestamp::FromEpochMilliseconds(inner->as_int() * 1000),
                               TimestampFormat::kEpochSeconds});

as_int() can be any value up to INT64_MAX (the unsigned path only rejects values above INT64_MAX), so inner->as_int() * 1000 overflows int64_tundefined behavior triggered by a ~9-byte payload. Under UBSan this aborts; in release it silently produces a garbage instant that then flows into Decompose/Format.

Failure scenario

A peer sends CBOR C1 1B 7F FF FF FF FF FF FF FF (tag 1 + a large uint64); * 1000 overflows. This is the one actual-UB finding in the runtime and is reachable from any untrusted CBOR body binding a timestamp member.

Related (same class, HIGH — fold in or split as you prefer)

  • runtime/src/core/document_serde.cc:22 + runtime/src/core/timestamp.cc:221-223: epoch-seconds timestamps forward any JSON/CBOR number to FromEpochSeconds, which does static_cast<int64_t>(std::llround(seconds * 1000.0)) with no range check; ParseEpochSeconds (timestamp.cc:190) accepts arbitrarily long digit strings (isfinite only). A value like 1e300 or 99999999999999999999 yields an unspecified instant instead of an error.
  • runtime/src/core/timestamp.cc:54-70,225-253: extreme instants format to 5+ digit / negative years via %04d, which no conformant peer (ParseDateTime requires 4-digit years) can round-trip — silent data corruption.

Suggested fix

Range-check before the multiply/cast in all three spots: reject (return Error::Serialization) when the epoch value, scaled to milliseconds, would fall outside the representable instant range, and bound the accepted year in Format/Decompose. A shared "checked seconds/millis → Timestamp" helper would cover the CBOR and JSON paths together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions