{"record":{"id":"04d5b5e246292e69","repo":"gitbutlerapp/gitbutler","slug":"u32-i32-conversion-overflow","errorCode":null,"errorMessage":"u32 -> i32 conversion overflow","messagePattern":"u32 -> i32 conversion overflow","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but-hunk-dependency/src/input.rs","lineNumber":60,"sourceCode":"pub struct InputDiffHunk {\n    /// The 1-based line number at which the previous version of the file started.\n    pub old_start: u32,\n    /// The non-zero amount of lines included in the previous version of the file.\n    pub old_lines: u32,\n    /// The 1-based line number at which the new version of the file started.\n    pub new_start: u32,\n    /// The non-zero amount of lines included in the new version of the file.\n    pub new_lines: u32,\n}\n\nimpl InputDiffHunk {\n    /// Compute the amount of lines that are left when subtracting old-lines from new-lines.\n    pub fn net_lines(&self) -> anyhow::Result<i32> {\n        // TODO: use `checked_signed_diff` instead when stable.\n        (self.new_lines as i64)\n            .checked_sub(self.old_lines as i64)\n            .and_then(|n| i32::try_from(n).ok())\n            .ok_or(anyhow!(\"u32 -> i32 conversion overflow\"))\n    }\n}\n\nimpl InputDiffHunk {\n    /// Create a new instance from unified `diff`.\n    pub fn from_unified_diff(\n        but_core::unified_diff::DiffHunk {\n            old_start,\n            old_lines,\n            new_start,\n            new_lines,\n            diff: _,\n        }: &but_core::unified_diff::DiffHunk,\n    ) -> Self {\n        InputDiffHunk {\n            old_start: *old_start,\n            old_lines: *old_lines,\n            new_start: *new_start,","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-hunk-dependency/src/input.rs#L42-L78","documentation":"InputDiffHunk::net_lines() computes new_lines - old_lines as an i64 and then narrows to i32. The error is thrown when the arithmetic subtraction would underflow/overflow at the i64 level or the result does not fit into an i32, which cannot happen with realistic u32 line counts but is guarded anyway because `checked_signed_diff` is not yet stable.","triggerScenarios":"Calling net_lines() on an InputDiffHunk whose new_lines/old_lines combination yields a value outside i32 range (theoretically |diff| > 2^31-1, impossible for real diffs since both operands are u32); in practice it indicates corrupted or adversarially constructed hunk input.","commonSituations":"Feeding fabricated or fuzzed diff data into the hunk-dependency machinery rather than real git diffs; deserializing InputDiffHunk from untrusted JSON with huge line counts.","solutions":["Verify the InputDiffHunk was built from a real unified diff, not synthetic values","Clamp or validate new_lines/old_lines before constructing the hunk","If truly needed, change the return type to i64 to eliminate the i32 narrowing"],"exampleFix":"// before\nlet net = hunk.net_lines()?;\n// after\nlet net = i64::from(hunk.new_lines()) - i64::from(hunk.old_lines()); // no i32 narrowing","handlingStrategy":"validation","validationCode":"fn net_lines_safe(new_lines: u32, old_lines: u32) -> Option<i32> {\n    let diff = i64::from(new_lines) - i64::from(old_lines);\n    i32::try_from(diff).ok()\n}\n// call net_lines() only when this returns Some","typeGuard":null,"tryCatchPattern":"// Rust: match on Result\nmatch hunk.net_lines() {\n    Ok(n) => use_net(n),\n    Err(e) => log::warn!(\"net_lines unavailable: {e:#}\"),\n}","preventionTips":["Only construct InputDiffHunk from real git diffs","Validate line-count fields when deserializing from untrusted sources"],"tags":["arithmetic-overflow","rust","integer-conversion"],"backgroundTag":"value-out-of-range","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}