oxidecomputer / oxidecomputer/omicron

Oximeter timeseries schema may in some cases only allow 254 target versions

Open
#10,967 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
572
Forks
97
Avg merge
2d 12h
Merged PRs (30d)
96

Description

version is defined as a NonZero<u8>, when parsing the timeseries definitions into a schema list it is zipped with 1u8.., this causes some issues since the current range implementation will panic one before the end when built with overflow-checks. This means that when running it in debug will not be able to parse a file with version 1 to and including 255, but it will do so in release.

This also means that it will give some rather odd diagnostics when reaching the limit such as

`Err` value: SchemaDefinition("Target 'target' versions should be sequential and monotonically increasing (expected 0, found 255)")

if you have more fields than 255. of course you cannot have a version 0 because it is a non-zero type.

A sidenote is that switching it to be a RangeFrom over NonZero<u8> will not resolve the issue as the RangeFrom will in that case saturate and may allow multiple version 255.

MRE for a panic with `overflow-checks` To be placed in `omicron/oximeter/schema/src/ir.rs` ```rust #[test] fn way_too_many_target_versions() { let contents = r#" format_version = 1
    [target]
    name = "target"
    description = "some target"
    authz_scope = "fleet"
    versions = [
        { version = 1, fields = [ "foo" ] },
        { version = 2, fields = [ "foo" ] },
        { version = 3, fields = [ "foo" ] },
        { version = 4, fields = [ "foo" ] },
        { version = 5, fields = [ "foo" ] },
        { version = 6, fields = [ "foo" ] },
        { version = 7, fields = [ "foo" ] },
        { version = 8, fields = [ "foo" ] },
        { version = 9, fields = [ "foo" ] },
        { version = 10, fields = [ "foo" ] },
        { version = 11, fields = [ "foo" ] },
        { version = 12, fields = [ "foo" ] },
        { version = 13, fields = [ "foo" ] },
        { version = 14, fields = [ "foo" ] },
        { version = 15, fields = [ "foo" ] },
        { version = 16, fields = [ "foo" ] },
        { version = 17, fields = [ "foo" ] },
        { version = 18, fields = [ "foo" ] },
        { version = 19, fields = [ "foo" ] },
        { version = 20, fields = [ "foo" ] },
        { version = 21, fields = [ "foo" ] },
        { version = 22, fields = [ "foo" ] },
        { version = 23, fields = [ "foo" ] },
        { version = 24, fields = [ "foo" ] },
        { version = 25, fields = [ "foo" ] },
        { version = 26, fields = [ "foo" ] },
        { version = 27, fields = [ "foo" ] },
        { version = 28, fields = [ "foo" ] },
        { version = 29, fields = [ "foo" ] },
        { version = 30, fields = [ "foo" ] },
        { version = 31, fields = [ "foo" ] },
        { version = 32, fields = [ "foo" ] },
        { version = 33, fields = [ "foo" ] },
        { version = 34, fields = [ "foo" ] },
        { version = 35, fields = [ "foo" ] },
        { version = 36, fields = [ "foo" ] },
        { version = 37, fields = [ "foo" ] },
        { version = 38, fields = [ "foo" ] },
        { version = 39, fields = [ "foo" ] },
        { version = 40, fields = [ "foo" ] },
        { version = 41, fields = [ "foo" ] },
        { version = 42, fields = [ "foo" ] },
        { version = 43, fields = [ "foo" ] },
        { version = 44, fields = [ "foo" ] },
        { version = 45, fields = [ "foo" ] },
        { version = 46, fields = [ "foo" ] },
        { version = 47, fields = [ "foo" ] },
        { version = 48, fields = [ "foo" ] },
        { version = 49, fields = [ "foo" ] },
        { version = 50, fields = [ "foo" ] },
        { version = 51, fields = [ "foo" ] },
        { version = 52, fields = [ "foo" ] },
        { version = 53, fields = [ "foo" ] },
        { version = 54, fields = [ "foo" ] },
        { version = 55, fields = [ "foo" ] },
        { version = 56, fields = [ "foo" ] },
        { version = 57, fields = [ "foo" ] },
        { version = 58, fields = [ "foo" ] },
        { version = 59, fields = [ "foo" ] },
        { version = 60, fields = [ "foo" ] },
        { version = 61, fields = [ "foo" ] },
        { version = 62, fields = [ "foo" ] },
        { version = 63, fields = [ "foo" ] },
        { version = 64, fields = [ "foo" ] },
        { version = 65, fields = [ "foo" ] },
        { version = 66, fields = [ "foo" ] },
        { version = 67, fields = [ "foo" ] },
        { version = 68, fields = [ "foo" ] },
        { version = 69, fields = [ "foo" ] },
        { version = 70, fields = [ "foo" ] },
        { version = 71, fields = [ "foo" ] },
        { version = 72, fields = [ "foo" ] },
        { version = 73, fields = [ "foo" ] },
        { version = 74, fields = [ "foo" ] },
        { version = 75, fields = [ "foo" ] },
        { version = 76, fields = [ "foo" ] },
        { version = 77, fields = [ "foo" ] },
        { version = 78, fields = [ "foo" ] },
        { version = 79, fields = [ "foo" ] },
        { version = 80, fields = [ "foo" ] },
        { version = 81, fields = [ "foo" ] },
        { version = 82, fields = [ "foo" ] },
        { version = 83, fields = [ "foo" ] },
        { version = 84, fields = [ "foo" ] },
        { version = 85, fields = [ "foo" ] },
        { version = 86, fields = [ "foo" ] },
        { version = 87, fields = [ "foo" ] },
        { version = 88, fields = [ "foo" ] },
        { version = 89, fields = [ "foo" ] },
        { version = 90, fields = [ "foo" ] },
        { version = 91, fields = [ "foo" ] },
        { version = 92, fields = [ "foo" ] },
        { version = 93, fields = [ "foo" ] },
        { version = 94, fields = [ "foo" ] },
        { version = 95, fields = [ "foo" ] },
        { version = 96, fields = [ "foo" ] },
        { version = 97, fields = [ "foo" ] },
        { version = 98, fields = [ "foo" ] },
        { version = 99, fields = [ "foo" ] },
        { version = 100, fields = [ "foo" ] },
        { version = 101, fields = [ "foo" ] },
        { version = 102, fields = [ "foo" ] },
        { version = 103, fields = [ "foo" ] },
        { version = 104, fields = [ "foo" ] },
        { version = 105, fields = [ "foo" ] },
        { version = 106, fields = [ "foo" ] },
        { version = 107, fields = [ "foo" ] },
        { version = 108, fields = [ "foo" ] },
        { version = 109, fields = [ "foo" ] },
        { version = 110, fields = [ "foo" ] },
        { version = 111, fields = [ "foo" ] },
        { version = 112, fields = [ "foo" ] },
        { version = 113, fields = [ "foo" ] },
        { version = 114, fields = [ "foo" ] },
        { version = 115, fields = [ "foo" ] },
        { version = 116, fields = [ "foo" ] },
        { version = 117, fields = [ "foo" ] },
        { version = 118, fields = [ "foo" ] },
        { version = 119, fields = [ "foo" ] },
        { version = 120, fields = [ "foo" ] },
        { version = 121, fields = [ "foo" ] },
        { version = 122, fields = [ "foo" ] },
        { version = 123, fields = [ "foo" ] },
        { version = 124, fields = [ "foo" ] },
        { version = 125, fields = [ "foo" ] },
        { version = 126, fields = [ "foo" ] },
        { version = 127, fields = [ "foo" ] },
        { version = 128, fields = [ "foo" ] },
        { version = 129, fields = [ "foo" ] },
        { version = 130, fields = [ "foo" ] },
        { version = 131, fields = [ "foo" ] },
        { version = 132, fields = [ "foo" ] },
        { version = 133, fields = [ "foo" ] },
        { version = 134, fields = [ "foo" ] },
        { version = 135, fields = [ "foo" ] },
        { version = 136, fields = [ "foo" ] },
        { version = 137, fields = [ "foo" ] },
        { version = 138, fields = [ "foo" ] },
        { version = 139, fields = [ "foo" ] },
        { version = 140, fields = [ "foo" ] },
        { version = 141, fields = [ "foo" ] },
        { version = 142, fields = [ "foo" ] },
        { version = 143, fields = [ "foo" ] },
        { version = 144, fields = [ "foo" ] },
        { version = 145, fields = [ "foo" ] },
        { version = 146, fields = [ "foo" ] },
        { version = 147, fields = [ "foo" ] },
        { version = 148, fields = [ "foo" ] },
        { version = 149, fields = [ "foo" ] },
        { version = 150, fields = [ "foo" ] },
        { version = 151, fields = [ "foo" ] },
        { version = 152, fields = [ "foo" ] },
        { version = 153, fields = [ "foo" ] },
        { version = 154, fields = [ "foo" ] },
        { version = 155, fields = [ "foo" ] },
        { version = 156, fields = [ "foo" ] },
        { version = 157, fields = [ "foo" ] },
        { version = 158, fields = [ "foo" ] },
        { version = 159, fields = [ "foo" ] },
        { version = 160, fields = [ "foo" ] },
        { version = 161, fields = [ "foo" ] },
        { version = 162, fields = [ "foo" ] },
        { version = 163, fields = [ "foo" ] },
        { version = 164, fields = [ "foo" ] },
        { version = 165, fields = [ "foo" ] },
        { version = 166, fields = [ "foo" ] },
        { version = 167, fields = [ "foo" ] },
        { version = 168, fields = [ "foo" ] },
        { version = 169, fields = [ "foo" ] },
        { version = 170, fields = [ "foo" ] },
        { version = 171, fields = [ "foo" ] },
        { version = 172, fields = [ "foo" ] },
        { version = 173, fields = [ "foo" ] },
        { version = 174, fields = [ "foo" ] },
        { version = 175, fields = [ "foo" ] },
        { version = 176, fields = [ "foo" ] },
        { version = 177, fields = [ "foo" ] },
        { version = 178, fields = [ "foo" ] },
        { version = 179, fields = [ "foo" ] },
        { version = 180, fields = [ "foo" ] },
        { version = 181, fields = [ "foo" ] },
        { version = 182, fields = [ "foo" ] },
        { version = 183, fields = [ "foo" ] },
        { version = 184, fields = [ "foo" ] },
        { version = 185, fields = [ "foo" ] },
        { version = 186, fields = [ "foo" ] },
        { version = 187, fields = [ "foo" ] },
        { version = 188, fields = [ "foo" ] },
        { version = 189, fields = [ "foo" ] },
        { version = 190, fields = [ "foo" ] },
        { version = 191, fields = [ "foo" ] },
        { version = 192, fields = [ "foo" ] },
        { version = 193, fields = [ "foo" ] },
        { version = 194, fields = [ "foo" ] },
        { version = 195, fields = [ "foo" ] },
        { version = 196, fields = [ "foo" ] },
        { version = 197, fields = [ "foo" ] },
        { version = 198, fields = [ "foo" ] },
        { version = 199, fields = [ "foo" ] },
        { version = 200, fields = [ "foo" ] },
        { version = 201, fields = [ "foo" ] },
        { version = 202, fields = [ "foo" ] },
        { version = 203, fields = [ "foo" ] },
        { version = 204, fields = [ "foo" ] },
        { version = 205, fields = [ "foo" ] },
        { version = 206, fields = [ "foo" ] },
        { version = 207, fields = [ "foo" ] },
        { version = 208, fields = [ "foo" ] },
        { version = 209, fields = [ "foo" ] },
        { version = 210, fields = [ "foo" ] },
        { version = 211, fields = [ "foo" ] },
        { version = 212, fields = [ "foo" ] },
        { version = 213, fields = [ "foo" ] },
        { version = 214, fields = [ "foo" ] },
        { version = 215, fields = [ "foo" ] },
        { version = 216, fields = [ "foo" ] },
        { version = 217, fields = [ "foo" ] },
        { version = 218, fields = [ "foo" ] },
        { version = 219, fields = [ "foo" ] },
        { version = 220, fields = [ "foo" ] },
        { version = 221, fields = [ "foo" ] },
        { version = 222, fields = [ "foo" ] },
        { version = 223, fields = [ "foo" ] },
        { version = 224, fields = [ "foo" ] },
        { version = 225, fields = [ "foo" ] },
        { version = 226, fields = [ "foo" ] },
        { version = 227, fields = [ "foo" ] },
        { version = 228, fields = [ "foo" ] },
        { version = 229, fields = [ "foo" ] },
        { version = 230, fields = [ "foo" ] },
        { version = 231, fields = [ "foo" ] },
        { version = 232, fields = [ "foo" ] },
        { version = 233, fields = [ "foo" ] },
        { version = 234, fields = [ "foo" ] },
        { version = 235, fields = [ "foo" ] },
        { version = 236, fields = [ "foo" ] },
        { version = 237, fields = [ "foo" ] },
        { version = 238, fields = [ "foo" ] },
        { version = 239, fields = [ "foo" ] },
        { version = 240, fields = [ "foo" ] },
        { version = 241, fields = [ "foo" ] },
        { version = 242, fields = [ "foo" ] },
        { version = 243, fields = [ "foo" ] },
        { version = 244, fields = [ "foo" ] },
        { version = 245, fields = [ "foo" ] },
        { version = 246, fields = [ "foo" ] },
        { version = 247, fields = [ "foo" ] },
        { version = 248, fields = [ "foo" ] },
        { version = 249, fields = [ "foo" ] },
        { version = 250, fields = [ "foo" ] },
        { version = 251, fields = [ "foo" ] },
        { version = 252, fields = [ "foo" ] },
        { version = 253, fields = [ "foo" ] },
        { version = 254, fields = [ "foo" ] },
        { version = 255, fields = [ "foo" ] },
        { version = 255, fields = [ "foo" ] },
    ]

    [[metrics]]
    name = "metric"
    description = "some metric"
    datum_type = "u8"
    units = "count"
    versions = [
        { added_in = 1, fields = [] },
    ]

    [fields.foo]
    type = "string"
    description = "a field"
    "#;
    load_schema(contents).unwrap();
}
thread 'ir::tests::way_too_many_target_versions' (941567) panicked at /rustc/8bab26f4f68e0e26f0bb7960be334d5b520ea452/library/core/src/iter/range.rs:438:1:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

A possible solution could be to add a explicit upper bound of 255 fields:

        let mut target_fields_by_version = BTreeMap::new();

        if self.target.versions.len() > 255 {
            return Err(MetricsError::SchemaDefinition(format!(
                "Target '{}' versions can only contain 255 versions (found {})",
                target_name, self.target.versions.len(),
            )));
        }
        // We know that it is at most 255
        let upper_bound = self.target.versions.len() as u8;
        
        for (expected_version, target_fields) in
            (1u8..upper_bound).zip(self.target.versions.iter())
        {

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in oximeter/schema/src/ir.rs at the target-version parsing and its zip with 1u8..; run the supplied way_too_many_target_versions reproducer with overflow checks enabled. Add coverage for the upper version limit and ensure parsing reports a sensible error rather than panicking or accepting invalid version sequences.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.