Files
OpenShell/crates/openshell-server/src/compute
Andrew White 76a5397e6f fix: assorted arithmetic and indexing robustness fixes (#2451)
* fix(core): avoid i64 underflow on expired GCP token

When a cached GCP access token was already expired (expires_at_ms < now),
the remaining lifetime calculation used plain subtraction:
(expires_at_ms - now) / 1000. This underflows in debug builds and wraps
in release builds.

Use saturating_sub so an expired token simply yields a non-positive
remaining lifetime and is skipped. Add a regression test.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): avoid i64 underflow in lease age calculation

The lease steal path computed age as now_ms() - record.updated_at_ms.
If the stored updated_at timestamp is in the future (e.g. clock skew),
the subtraction underflows. Use saturating_sub to treat a future
timestamp as age 0, keeping the lease considered fresh.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): avoid i64 overflow on large credential max lifetime

max_lifetime_seconds is only validated as >= 0. The code multiplied the
raw i64 value by 1000 to compute max_expires, which overflows for
values above i64::MAX / 1000. The capped i32 value used for the STS
request was already computed but not reused.

Compute max_lifetime_ms from the capped value with saturating_mul and
use it for both expires_at fallback and max_expires.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): avoid hardcoded indexing into base_url_config_keys

resolve_vertex_ai_route assumed every inference provider profile has at
least two base URL config keys by indexing [0] and [1]. Only the Vertex
profile satisfies that today; other profiles would panic here.

Iterate base_url_config_keys with find_map instead, and add a test with
an empty key list to ensure no panic.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): clamp lease age at zero for future timestamps

Address review feedback: add regression coverage for future-timestamp
lease clock skew. Extract lease_is_expired() and clamp the age at zero —
i64::saturating_sub saturates at i64::MIN, not zero, so a future
updated_at_ms must be clamped explicitly to be treated as age zero and
remain unstealable with a positive TTL.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): fall back past blank preferred Vertex base URL

Address review feedback: find_map stopped at the first present key even
when its value was blank, and the outer filter discarded it without
considering lower-priority aliases. Filter blank values inside the
closure so a blank preferred key falls back to a valid alias, matching
the documented priority order. Add a regression test with a blank
preferred key and a valid fallback.

Signed-off-by: Andrew White <andrewh@cdw.com>

* fix(server): saturate AWS STS expiry cap on saturated clock

Address review feedback: now_ms + max_lifetime_ms could still overflow
when current_time_ms() saturates near i64::MAX. Compute
max_expires = now_ms.saturating_add(max_lifetime_ms) once and use it for
both the conversion fallback and the expiry cap. Add a boundary unit
test covering a saturated clock.

Signed-off-by: Andrew White <andrewh@cdw.com>

---------

Signed-off-by: Andrew White <andrewh@cdw.com>
2026-07-27 14:39:40 +00:00
..