Skip to content

Return an error instead of panicking on invalid transform-origin - #65

Open
ChrisJr404 wants to merge 1 commit into
linebender:mainfrom
ChrisJr404:fix-transform-origin-panic
Open

ChrisJr404 wants to merge 1 commit into
linebender:mainfrom
ChrisJr404:fix-transform-origin-panic

Conversation

@ChrisJr404

Copy link
Copy Markdown

TransformOrigin::from_str can panic on a number of short, otherwise-innocuous inputs. For example:

use std::str::FromStr;
use svgtypes::TransformOrigin;

let _ = TransformOrigin::from_str(" ");  // panics
let _ = TransformOrigin::from_str("\t"); // panics
let _ = TransformOrigin::from_str("-");  // panics
let _ = TransformOrigin::from_str("+");  // panics

Each of these hits the unreachable!() arm:

thread 'main' panicked at src/transform_origin.rs:184:18:
internal error: entered unreachable code

Why it happens

At the top of from_str we bail out early only when the input is completely empty. For a non-empty input the first component is parsed with parse_part, which tries parse_directional_position and then parse_length. parse_length calls skip_spaces() (and consumes a leading sign) before it fails, so on an input like " " or "-" the parse returns Err/None but the stream cursor has already advanced to the end.

Because the stream is now at its end, both of the if !stream.at_end() blocks that would parse the second and third components are skipped, leaving first_arg == None and second_arg == third_arg == None. The final match only handles the (Some, None, None) and (Some, Some, _) shapes, so this falls through to _ => unreachable!() and panics.

Since from_str runs on untrusted attribute values (e.g. a transform-origin presentation attribute), a caller that does not wrap the parse in catch_unwind can be taken down by a one-byte input.

Fix

Return the already-defined TransformOriginError::InvalidParameters for that fall-through case instead of panicking. Valid inputs are unaffected — they always produce a Some first component and hit the existing arms.

Added regression tests for " ", " ", "\t", "-", and "+". The full test suite passes and cargo fmt/cargo clippy --lib --tests are clean.

TransformOrigin::from_str could hit an unreachable!() and panic on
trivial inputs such as " ", "\t", "-", or "+". When the first
component fails to parse, the underlying skip_spaces/sign handling can
still advance the stream to its end, so both follow-up blocks are
skipped and the final match falls through to the unreachable arm with
no parsed arguments.

Return TransformOriginError::InvalidParameters for that case and add
regression tests covering the offending inputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant