forked from 1b-agent/opencadstudio-upstream
fix(plot-style): bound custom lineweight table keys to u8 range
Bug: Custom lineweight table entries in a CTB are keyed by a number parsed from the file and stored without an upper bound, but the only reader (resolve_lineweight) indexes through entry.lineweight: u8. A key beyond u8::MAX (e.g. 18446744073709551615 in a crafted file) caused an out-of-bounds-style failure or a multi-GB table allocation when the style was applied. Reproduce: 1. Craft a CTB whose custom lineweight table uses a huge key (usize::MAX). 2. Apply the style or open plot settings. 3. Before: overflow panic (or a giant allocation). 4. After: the key is out of range, the line is ignored like any other malformed CTB line, and the style loads. Fix: Keys are bounded to u8::MAX when parsed - exactly the range the u8-typed reader can ever use. In-range keys behave as before; keys 256 and above are dropped. Tests: a usize::MAX key does not overflow, an in-range key is kept and applied, 256 is ignored.
This commit is contained in:
1 parent
ac6aa750f6
commit
790da218b5
1 file changed
+42
-3
+42
-3
@@ -785,11 +785,19 @@ fn parse_plot_style_text(text: &str, name: String, is_stb: bool) -> Result<PlotS
|
|||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Section::Lineweights => {
|
Section::Lineweights => {
|
||||||
|
// The key is file-controlled: `18446744073709551615` made
|
||||||
|
// `index + 1` overflow (debug panic, release wrap to
|
||||||
|
// `resize(0)` then OOB), and `500000000` resized a 2 GB
|
||||||
|
// table. Entries past 255 are unreachable — the consumer
|
||||||
|
// indexes with `PlotStyleEntry.lineweight: u8` — so
|
||||||
|
// oversized keys are ignored like other malformed lines.
|
||||||
if let (Ok(index), Ok(weight)) = (key.parse::<usize>(), value.parse::<f32>()) {
|
if let (Ok(index), Ok(weight)) = (key.parse::<usize>(), value.parse::<f32>()) {
|
||||||
if lineweights.len() <= index {
|
if index <= u8::MAX as usize {
|
||||||
lineweights.resize(index + 1, 0.0);
|
if lineweights.len() <= index {
|
||||||
|
lineweights.resize(index + 1, 0.0);
|
||||||
|
}
|
||||||
|
lineweights[index] = weight;
|
||||||
}
|
}
|
||||||
lineweights[index] = weight;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -1088,3 +1096,34 @@ mod inflate_limit_tests {
|
|||||||
assert!(error.contains("limit"), "{error}");
|
assert!(error.contains("limit"), "{error}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod lineweight_table_tests {
|
||||||
|
use super::{parse_plot_style_text, LW_TABLE};
|
||||||
|
|
||||||
|
fn parse(text: &str) -> super::PlotStyleTable {
|
||||||
|
parse_plot_style_text(text, "crafted.ctb".into(), false).expect("parses")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The table key is file-controlled: `18446744073709551615` made
|
||||||
|
/// `index + 1` overflow — debug builds panic, release wraps to
|
||||||
|
/// `resize(0)` and then panics OOB at `lineweights[usize::MAX]` — and
|
||||||
|
/// `500000000` resized a 2 GB table (allocation abort). Entries past
|
||||||
|
/// 255 are unreachable anyway: `PlotStyleEntry.lineweight` is a `u8`.
|
||||||
|
#[test]
|
||||||
|
fn oversized_lineweight_table_keys_are_ignored() {
|
||||||
|
let table = parse("custom_lineweight_table{\n18446744073709551615=0.5\n}\n");
|
||||||
|
assert_eq!(table.lineweights, LW_TABLE.to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In-range keys still extend the table; 256 (beyond `u8`) is ignored.
|
||||||
|
#[test]
|
||||||
|
fn in_range_lineweight_table_keys_are_kept() {
|
||||||
|
let table = parse("custom_lineweight_table{\n30=0.7\n}\n");
|
||||||
|
assert_eq!(table.lineweights.len(), 31);
|
||||||
|
assert_eq!(table.lineweights[30], 0.7);
|
||||||
|
|
||||||
|
let table = parse("custom_lineweight_table{\n256=0.7\n}\n");
|
||||||
|
assert_eq!(table.lineweights, LW_TABLE.to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user