From 727e8012cd85d9e38ed0086ae67cefbcef9dcc06 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 00:26:25 +0900 Subject: [PATCH 1/5] test(utility): require fail-closed numeric controls --- tests/test_utility_control_boundary.py | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/test_utility_control_boundary.py diff --git a/tests/test_utility_control_boundary.py b/tests/test_utility_control_boundary.py new file mode 100644 index 000000000..7bb8345f0 --- /dev/null +++ b/tests/test_utility_control_boundary.py @@ -0,0 +1,74 @@ +"""Trust-boundary regressions for classical selection utility controls.""" + +from __future__ import annotations + +import math + +import numpy as np +import pytest + +from fast_mlsirm.utility import selection_utility, taylor_russell + + +class _HostileFloat: + """Arbitrary float-protocol object whose callback must stay unreachable.""" + + calls = 0 + + @classmethod + def reset(cls) -> None: + """Reset the callback counter.""" + cls.calls = 0 + + def __float__(self) -> float: + type(self).calls += 1 + raise AssertionError("caller __float__ callback must not execute") + + +@pytest.mark.parametrize( + "call", + ( + lambda value: selection_utility(value, 1.0, 0.5, 0.5), + lambda value: taylor_russell(value, 0.5, 0.5), + ), +) +def test_utility_rejects_float_protocol_objects_without_callbacks(call) -> None: + """Untrusted float-protocol objects fail before any caller callback executes.""" + _HostileFloat.reset() + + with pytest.raises(ValueError, match="must be a finite real number"): + call(_HostileFloat()) + + assert _HostileFloat.calls == 0 + + +@pytest.mark.parametrize( + "call", + ( + lambda value: selection_utility(value, 1.0, 0.5, 0.5), + lambda value: taylor_russell(value, 0.5, 0.5), + ), +) +@pytest.mark.parametrize("value", (True, False, math.inf, -math.inf, math.nan)) +def test_utility_rejects_boolean_and_nonfinite_controls(call, value) -> None: + """Boolean and non-finite scalars are rejected by the Python trust boundary.""" + with pytest.raises(ValueError, match="must be a finite real number"): + call(value) + + +def test_numpy_real_scalars_preserve_native_results() -> None: + """Trusted NumPy real scalars produce the same Rust-owned results as floats.""" + py_utility = selection_utility(10.0, 2.0, 0.4, 0.5, 1.0, 2.0) + np_utility = selection_utility( + np.float64(10.0), + np.float64(2.0), + np.float64(0.4), + np.float64(0.5), + np.float64(1.0), + np.float64(2.0), + ) + assert np_utility == py_utility + + py_tr = taylor_russell(0.4, 0.5, 0.6) + np_tr = taylor_russell(np.float64(0.4), np.float64(0.5), np.float64(0.6)) + assert np_tr == py_tr From 7fcc69e887e459d17e161cc8d52036dd2c00b2e6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 00:27:01 +0900 Subject: [PATCH 2/5] fix(utility): validate scalars before Rust discovery --- python/fast_mlsirm/utility.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/python/fast_mlsirm/utility.py b/python/fast_mlsirm/utility.py index c4152203b..4a1375e45 100644 --- a/python/fast_mlsirm/utility.py +++ b/python/fast_mlsirm/utility.py @@ -36,6 +36,8 @@ from __future__ import annotations +import math +import numbers from dataclasses import dataclass @@ -67,6 +69,16 @@ class TaylorRussellResult: q_joint: float +def _coerce_finite_real(value: object, *, name: str) -> float: + """Return a trusted finite real scalar without invoking arbitrary protocols.""" + if isinstance(value, bool) or not isinstance(value, numbers.Real): + raise ValueError(f"{name} must be a finite real number") + marshaled = float(value) + if not math.isfinite(marshaled): + raise ValueError(f"{name} must be a finite real number") + return marshaled + + def selection_utility( n: float, sdy: float, @@ -84,10 +96,22 @@ def selection_utility( applicant" but never multiplies by ``n``; we document the actual semantics), ``period`` expected tenure (>= 1). """ + marshaled_n = _coerce_finite_real(n, name="n") + marshaled_sdy = _coerce_finite_real(sdy, name="sdy") + marshaled_rxy = _coerce_finite_real(rxy, name="rxy") + marshaled_sr = _coerce_finite_real(sr, name="sr") + marshaled_cost_total = _coerce_finite_real(cost_total, name="cost_total") + marshaled_period = _coerce_finite_real(period, name="period") + from . import _core r = _core.selection_utility( - float(n), float(sdy), float(rxy), float(sr), float(cost_total), float(period) + marshaled_n, + marshaled_sdy, + marshaled_rxy, + marshaled_sr, + marshaled_cost_total, + marshaled_period, ) return SelectionUtilityResult( xc=r["xc"], ux=r["ux"], pux=r["pux"], utility_gain=r["utility_gain"] @@ -101,9 +125,13 @@ def taylor_russell(rxy: float, sr: float, br: float) -> TaylorRussellResult: base rate of success in (0, 1). At ``rxy = 0`` the success ratio equals ``br`` (no selection information). """ + marshaled_rxy = _coerce_finite_real(rxy, name="rxy") + marshaled_sr = _coerce_finite_real(sr, name="sr") + marshaled_br = _coerce_finite_real(br, name="br") + from . import _core - r = _core.taylor_russell(float(rxy), float(sr), float(br)) + r = _core.taylor_russell(marshaled_rxy, marshaled_sr, marshaled_br) return TaylorRussellResult( success_ratio=r["success_ratio"], base_rate=r["base_rate"], From 2282a268b3d5bd12724dd4acded5d2a312f80cc6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 00:28:18 +0900 Subject: [PATCH 3/5] docs(changelog): record utility control hardening --- docs/changelog.d/974-utility-control-boundary.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/changelog.d/974-utility-control-boundary.md diff --git a/docs/changelog.d/974-utility-control-boundary.md b/docs/changelog.d/974-utility-control-boundary.md new file mode 100644 index 000000000..452ce8a02 --- /dev/null +++ b/docs/changelog.d/974-utility-control-boundary.md @@ -0,0 +1,6 @@ +# Selection utility numeric trust boundary + +## Fixed + +- Hardened classical selection-utility and Taylor-Russell scalar controls so booleans, non-real objects, and non-finite values fail with package-owned validation before compiled Rust discovery. +- Prevented arbitrary caller-defined `__float__` callbacks from executing during public control marshalling while preserving genuine Python/NumPy real scalar compatibility and keeping all BCG, Naylor-Shine, and Taylor-Russell arithmetic Rust-owned. From 833b900ad9c8651b76b583295f345216938c23fc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 07:32:51 +0900 Subject: [PATCH 4/5] test(utility): cover every scalar trust boundary --- tests/test_utility_control_boundary.py | 57 +++++++++++++++++--------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/tests/test_utility_control_boundary.py b/tests/test_utility_control_boundary.py index 7bb8345f0..23ce5fa97 100644 --- a/tests/test_utility_control_boundary.py +++ b/tests/test_utility_control_boundary.py @@ -25,33 +25,52 @@ def __float__(self) -> float: raise AssertionError("caller __float__ callback must not execute") -@pytest.mark.parametrize( - "call", - ( - lambda value: selection_utility(value, 1.0, 0.5, 0.5), - lambda value: taylor_russell(value, 0.5, 0.5), - ), +class _HostileFloatSubclass(float): + """Float subclass whose conversion callback must stay unreachable.""" + + calls = 0 + + @classmethod + def reset(cls) -> None: + """Reset the callback counter.""" + cls.calls = 0 + + def __float__(self) -> float: + type(self).calls += 1 + raise AssertionError("caller float-subclass __float__ callback must not execute") + + +_UTILITY_CONTROL_CALLS = ( + lambda value: selection_utility(value, 1.0, 0.5, 0.5), + lambda value: selection_utility(1.0, value, 0.5, 0.5), + lambda value: selection_utility(1.0, 1.0, value, 0.5), + lambda value: selection_utility(1.0, 1.0, 0.5, value), + lambda value: selection_utility(1.0, 1.0, 0.5, 0.5, value, 1.0), + lambda value: selection_utility(1.0, 1.0, 0.5, 0.5, 0.0, value), + lambda value: taylor_russell(value, 0.5, 0.5), + lambda value: taylor_russell(0.4, value, 0.5), + lambda value: taylor_russell(0.4, 0.5, value), ) -def test_utility_rejects_float_protocol_objects_without_callbacks(call) -> None: - """Untrusted float-protocol objects fail before any caller callback executes.""" - _HostileFloat.reset() + + +@pytest.mark.parametrize("call", _UTILITY_CONTROL_CALLS) +@pytest.mark.parametrize("hostile_type", (_HostileFloat, _HostileFloatSubclass)) +def test_utility_rejects_float_protocol_objects_without_callbacks( + call, hostile_type +) -> None: + """Every scalar position rejects hostile conversion without callback execution.""" + hostile_type.reset() with pytest.raises(ValueError, match="must be a finite real number"): - call(_HostileFloat()) + call(hostile_type()) - assert _HostileFloat.calls == 0 + assert hostile_type.calls == 0 -@pytest.mark.parametrize( - "call", - ( - lambda value: selection_utility(value, 1.0, 0.5, 0.5), - lambda value: taylor_russell(value, 0.5, 0.5), - ), -) +@pytest.mark.parametrize("call", _UTILITY_CONTROL_CALLS) @pytest.mark.parametrize("value", (True, False, math.inf, -math.inf, math.nan)) def test_utility_rejects_boolean_and_nonfinite_controls(call, value) -> None: - """Boolean and non-finite scalars are rejected by the Python trust boundary.""" + """Every scalar position rejects boolean and non-finite control values.""" with pytest.raises(ValueError, match="must be a finite real number"): call(value) From 92e30348cc50727d77a0a892713b2e917764eada Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 07:33:23 +0900 Subject: [PATCH 5/5] fix(utility): reject real scalar subclasses before conversion --- python/fast_mlsirm/utility.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/python/fast_mlsirm/utility.py b/python/fast_mlsirm/utility.py index 4a1375e45..6aa2834e5 100644 --- a/python/fast_mlsirm/utility.py +++ b/python/fast_mlsirm/utility.py @@ -37,9 +37,28 @@ from __future__ import annotations import math -import numbers from dataclasses import dataclass +import numpy as np + + +_TRUSTED_REAL_SCALAR_TYPES = ( + int, + float, + np.int8, + np.int16, + np.int32, + np.int64, + np.uint8, + np.uint16, + np.uint32, + np.uint64, + np.float16, + np.float32, + np.float64, + np.longdouble, +) + @dataclass class SelectionUtilityResult: @@ -48,7 +67,7 @@ class SelectionUtilityResult: ``xc`` is the standard-normal predictor cutoff ``Phi^-1(1 - sr)``; ``ux`` the selection intensity ``phi(xc)/sr`` (mean standardized predictor of those selected); ``pux = rxy * ux`` the Naylor-Shine mean - standardized criterion of those selected; ``utility_gain`` the BCG + standardized criterion of those selected); ``utility_gain`` the BCG gain ``n * period * sdy * pux - cost_total``.""" xc: float @@ -70,8 +89,8 @@ class TaylorRussellResult: def _coerce_finite_real(value: object, *, name: str) -> float: - """Return a trusted finite real scalar without invoking arbitrary protocols.""" - if isinstance(value, bool) or not isinstance(value, numbers.Real): + """Return a trusted finite real scalar without invoking caller protocols.""" + if type(value) not in _TRUSTED_REAL_SCALAR_TYPES: raise ValueError(f"{name} must be a finite real number") marshaled = float(value) if not math.isfinite(marshaled):