fix: close STEP-073 QBIN2 V4 validator audit

This commit is contained in:
OpenAI Codex
2026-09-13 10:18:33 +08:00
parent 1a453a6623
commit 4b8d200831
3 changed files with 126 additions and 13 deletions

View File

@@ -18,6 +18,9 @@ enum class FieldProfileValidationCode : uint8_t {
UndeclaredRequiredPlane, UndeclaredRequiredPlane,
AdditionalPlaneNotAllowed, AdditionalPlaneNotAllowed,
InvalidVelocityGroup, InvalidVelocityGroup,
// A reserved Native Core semanticID was declared with a descriptor that
// conflicts with its protocol-defined physical meaning.
SemanticContractMismatch,
InvalidCoreUnit, InvalidCoreUnit,
UnsupportedSharedValidityMode, UnsupportedSharedValidityMode,
TimeRelationMismatch, TimeRelationMismatch,

View File

@@ -1,6 +1,9 @@
#include <mbgl/particle/field/field_profile_validator.hpp> #include <mbgl/particle/field/field_profile_validator.hpp>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <iterator>
#include <limits>
#include <map> #include <map>
namespace mbgl::particle { namespace mbgl::particle {
@@ -8,6 +11,43 @@ namespace {
using PlaneRef = const DecodedFieldPlane*; using PlaneRef = const DecodedFieldPlane*;
// These are protocol-reserved Core semantic contracts, deliberately separate
// from product/profile resolution. Unknown semantic IDs remain extensible.
struct CoreSemanticContract {
uint16_t semanticID;
FieldValueClass valueClass;
uint16_t unitID;
FieldComponent component;
bool groupMustBeZero;
};
constexpr CoreSemanticContract CoreSemanticContracts[] = {
{1, FieldValueClass::VelocityComponent, FieldUnitMetersPerSecond, FieldComponent::UEast, false},
{2, FieldValueClass::VelocityComponent, FieldUnitMetersPerSecond, FieldComponent::VNorth, false},
{3, FieldValueClass::Validity, FieldUnitNone, FieldComponent::None, true},
{10, FieldValueClass::DirectionAngle, FieldUnitDegrees, FieldComponent::None, true},
{11, FieldValueClass::DirectionAngle, FieldUnitDegrees, FieldComponent::None, true},
};
const CoreSemanticContract* coreSemanticContract(const uint16_t semanticID) {
const auto found = std::find_if(std::begin(CoreSemanticContracts), std::end(CoreSemanticContracts),
[semanticID](const CoreSemanticContract& contract) {
return contract.semanticID == semanticID;
});
return found == std::end(CoreSemanticContracts) ? nullptr : &*found;
}
bool matchesCoreSemanticContract(const uint16_t semanticID,
const FieldValueClass valueClass,
const uint16_t unitID,
const uint16_t groupID,
const FieldComponent component) {
const auto* contract = coreSemanticContract(semanticID);
return !contract ||
(contract->valueClass == valueClass && contract->unitID == unitID && contract->component == component &&
(!contract->groupMustBeZero || groupID == 0));
}
bool sameKey(const FieldPlaneRequirement& requirement, const DecodedFieldPlane& plane) { bool sameKey(const FieldPlaneRequirement& requirement, const DecodedFieldPlane& plane) {
return requirement.semanticID == plane.key.semanticID && requirement.groupID == plane.key.groupID; return requirement.semanticID == plane.key.semanticID && requirement.groupID == plane.key.groupID;
} }
@@ -26,6 +66,12 @@ bool isNumeric(const FieldDType dtype) {
return dtype != FieldDType::Bitset; return dtype != FieldDType::Bitset;
} }
bool directionScaleIsCanonical(const float scale) {
constexpr float canonical = 360.0f / 65536.0f;
constexpr float tolerance = 4.0f * std::numeric_limits<float>::epsilon();
return std::fabs(scale - canonical) <= tolerance;
}
} // namespace } // namespace
FieldProfileValidationResult FieldProfileValidator::validate(const ResolvedFieldProfileContract& contract, FieldProfileValidationResult FieldProfileValidator::validate(const ResolvedFieldProfileContract& contract,
@@ -34,10 +80,13 @@ FieldProfileValidationResult FieldProfileValidator::validate(const ResolvedField
return result(FieldProfileValidationCode::UnsupportedContractSchema, "contractSchemaVersion"); return result(FieldProfileValidationCode::UnsupportedContractSchema, "contractSchemaVersion");
if (!contract.isStructurallyValid()) return result(FieldProfileValidationCode::InvalidContract, "contract"); if (!contract.isStructurallyValid()) return result(FieldProfileValidationCode::InvalidContract, "contract");
if (!frame.isStructurallyValid()) return result(FieldProfileValidationCode::InvalidFrame, "frame"); if (!frame.isStructurallyValid()) return result(FieldProfileValidationCode::InvalidFrame, "frame");
for (const auto& required : contract.requiredPlanes) {
if (!matchesCoreSemanticContract(required.semanticID, required.valueClass, required.unitID, required.groupID,
required.component))
return result(FieldProfileValidationCode::SemanticContractMismatch, "required Core semantic descriptor");
}
if (!contract.sharedValidityRequired) if (!contract.sharedValidityRequired)
return result(FieldProfileValidationCode::UnsupportedSharedValidityMode, "sharedValidityRequired=false"); return result(FieldProfileValidationCode::UnsupportedSharedValidityMode, "sharedValidityRequired=false");
if (frame.validityPlane.valueClass != FieldValueClass::Validity)
return result(FieldProfileValidationCode::InvalidCoreUnit, "validity value class");
if ((contract.timeRelation == FieldTimeRelation::ValidGteReference && if ((contract.timeRelation == FieldTimeRelation::ValidGteReference &&
frame.frameContentIdentity.validTimeMs < frame.referenceTimeMs) || frame.frameContentIdentity.validTimeMs < frame.referenceTimeMs) ||
@@ -60,6 +109,9 @@ FieldProfileValidationResult FieldProfileValidator::validate(const ResolvedField
} }
for (const auto plane : actual) { for (const auto plane : actual) {
if (!matchesCoreSemanticContract(plane->key.semanticID, plane->valueClass, plane->unitID, plane->key.groupID,
plane->key.component))
return result(FieldProfileValidationCode::SemanticContractMismatch, "actual Core semantic descriptor");
const auto declared = std::find_if(contract.requiredPlanes.begin(), contract.requiredPlanes.end(), const auto declared = std::find_if(contract.requiredPlanes.begin(), contract.requiredPlanes.end(),
[plane](const FieldPlaneRequirement& requirement) { [plane](const FieldPlaneRequirement& requirement) {
return matches(requirement, *plane); return matches(requirement, *plane);
@@ -74,8 +126,9 @@ FieldProfileValidationResult FieldProfileValidator::validate(const ResolvedField
plane->key.component != FieldComponent::None) plane->key.component != FieldComponent::None)
return result(FieldProfileValidationCode::InvalidCoreUnit, "validity descriptor"); return result(FieldProfileValidationCode::InvalidCoreUnit, "validity descriptor");
} else if (plane->valueClass == FieldValueClass::DirectionAngle) { } else if (plane->valueClass == FieldValueClass::DirectionAngle) {
if (plane->unitID != FieldUnitDegrees || !isNumeric(plane->dtype)) if (plane->unitID != FieldUnitDegrees || plane->key.component != FieldComponent::None || plane->key.groupID != 0 ||
return result(FieldProfileValidationCode::InvalidCoreUnit, "direction unit/dtype"); plane->dtype != FieldDType::UInt16 || plane->offset != 0.0f || !directionScaleIsCanonical(plane->scale))
return result(FieldProfileValidationCode::InvalidCoreUnit, "direction circular quantization descriptor");
} else if (plane->valueClass == FieldValueClass::VelocityComponent) { } else if (plane->valueClass == FieldValueClass::VelocityComponent) {
if (plane->unitID != FieldUnitMetersPerSecond || !isNumeric(plane->dtype)) if (plane->unitID != FieldUnitMetersPerSecond || !isNumeric(plane->dtype))
return result(FieldProfileValidationCode::InvalidCoreUnit, "velocity unit/dtype"); return result(FieldProfileValidationCode::InvalidCoreUnit, "velocity unit/dtype");

View File

@@ -90,34 +90,36 @@ TEST(ParticleFieldProfileValidator, EnforcesVelocityDirectionAndValidityCoreRule
EXPECT_EQ(particle::FieldUnitMetersPerSecond, 1); EXPECT_EQ(particle::FieldUnitMetersPerSecond, 1);
EXPECT_EQ(particle::FieldUnitDegrees, 4); EXPECT_EQ(particle::FieldUnitDegrees, 4);
auto c = contract(); auto f = frame(); const auto bytes = f.planes.front().storage; auto c = contract(); auto f = frame(); const auto bytes = f.planes.front().storage;
f.planes.push_back(plane(10, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3, f.planes.push_back(plane(100, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3,
particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes));
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup);
f.planes.push_back(plane(11, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3, f.planes.push_back(plane(101, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3,
particle::FieldComponent::VNorth, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::VNorth, particle::FieldDType::Int16, false, bytes));
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::Accepted); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::Accepted);
f.planes.back().unitID = 99; f.planes.back().unitID = 99;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
f = frame(); f = frame();
f.planes.push_back(plane(10, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 0, f.planes.push_back(plane(100, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 0,
particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes));
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup);
f = frame(); f = frame();
f.planes.push_back(plane(10, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3, f.planes.push_back(plane(100, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3,
particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes));
f.planes.push_back(plane(11, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3, f.planes.push_back(plane(101, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3,
particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::UEast, particle::FieldDType::Int16, false, bytes));
f.planes.push_back(plane(12, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3, f.planes.push_back(plane(102, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 3,
particle::FieldComponent::VNorth, particle::FieldDType::Int16, false, bytes)); particle::FieldComponent::VNorth, particle::FieldDType::Int16, false, bytes));
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidVelocityGroup);
f = frame(); f.planes.front().valueClass = particle::FieldValueClass::DirectionAngle; f = frame(); f.planes.front().key.semanticID = 10; f.planes.front().valueClass = particle::FieldValueClass::DirectionAngle;
f.planes.front().unitID = particle::FieldUnitDegrees; f.planes.front().unitID = particle::FieldUnitDegrees;
c.requiredPlanes.front().valueClass = particle::FieldValueClass::DirectionAngle; f.planes.front().dtype = particle::FieldDType::UInt16;
f.planes.front().scale = 360.0f / 65536.0f;
c.requiredPlanes.front().semanticID = 10; c.requiredPlanes.front().valueClass = particle::FieldValueClass::DirectionAngle;
c.requiredPlanes.front().unitID = particle::FieldUnitDegrees; c.requiredPlanes.front().unitID = particle::FieldUnitDegrees;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::Accepted); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::Accepted);
f.planes.front().unitID = 2; f.planes.front().unitID = 2;
c.requiredPlanes.front().unitID = 2; c.requiredPlanes.front().unitID = 2;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
c = contract(); f = frame(); f.validityPlane.dtype = particle::FieldDType::UInt8; c = contract(); f = frame(); f.validityPlane.dtype = particle::FieldDType::UInt8;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
f = frame(); f.validityPlane.unitID = 1; f = frame(); f.validityPlane.unitID = 1;
@@ -126,6 +128,61 @@ TEST(ParticleFieldProfileValidator, EnforcesVelocityDirectionAndValidityCoreRule
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit); EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
} }
TEST(ParticleFieldProfileValidator, EnforcesReservedCoreSemanticContractsFromProtocolTable) {
auto c = contract(); auto f = frame();
// Contract and frame agree on the spoof, but reserved semantic 1 remains
// VELOCITY_COMPONENT/MPS/U_EAST and cannot become an opaque scalar.
c.requiredPlanes.front().semanticID = 1;
f.planes.front().key.semanticID = 1;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
c = contract(); f = frame();
c.requiredPlanes.front() = {2, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 4,
particle::FieldComponent::UEast, true};
f.planes.front() = plane(2, particle::FieldValueClass::VelocityComponent, particle::FieldUnitMetersPerSecond, 4,
particle::FieldComponent::UEast, particle::FieldDType::Int16, true,
f.planes.front().storage, "u-spoof");
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
c = contract(); f = frame();
c.requiredPlanes.back().semanticID = 3;
c.requiredPlanes.back().valueClass = particle::FieldValueClass::Scalar;
f.validityPlane.key.semanticID = 3;
f.validityPlane.valueClass = particle::FieldValueClass::Scalar;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
}
TEST(ParticleFieldProfileValidator, EnforcesDirectionCircularQuantizationContract) {
auto c = contract(); auto f = frame();
c.requiredPlanes.front() = {10, particle::FieldValueClass::DirectionAngle, particle::FieldUnitDegrees, 0,
particle::FieldComponent::None, true};
f.planes.front() = plane(10, particle::FieldValueClass::DirectionAngle, particle::FieldUnitDegrees, 0,
particle::FieldComponent::None, particle::FieldDType::UInt16, true,
f.planes.front().storage, "direction");
f.planes.front().scale = 360.0f / 65536.0f;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::Accepted);
f.planes.front().unitID = particle::FieldUnitMetersPerSecond;
c.requiredPlanes.front().unitID = particle::FieldUnitMetersPerSecond;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
f.planes.front().unitID = particle::FieldUnitDegrees;
c.requiredPlanes.front().unitID = particle::FieldUnitDegrees;
f.planes.front().key.component = particle::FieldComponent::UEast;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
f.planes.front().key.component = particle::FieldComponent::None;
f.planes.front().key.groupID = 1;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::SemanticContractMismatch);
f.planes.front().key.groupID = 0;
f.planes.front().dtype = particle::FieldDType::UInt8;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
f.planes.front().dtype = particle::FieldDType::UInt16;
f.planes.front().scale = 0.1f;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
f.planes.front().scale = 360.0f / 65536.0f;
f.planes.front().offset = 1.0f;
EXPECT_EQ(validate(c, f), particle::FieldProfileValidationCode::InvalidCoreUnit);
}
TEST(ParticleFieldProfileValidator, UsesSemanticAndGroupAsTheOnlyPlaneIdentity) { TEST(ParticleFieldProfileValidator, UsesSemanticAndGroupAsTheOnlyPlaneIdentity) {
auto f = frame(); auto f = frame();
auto duplicate = f.planes.front(); auto duplicate = f.planes.front();