Repository navigation
Make wrap decoding exact when the sum leaves int32 - #1238
Merged
Merged
Conversation
PredictionSchemeWrapDecodingTransform::ComputeOriginalValue() adds the predicted value and the correction in uint32_t, to avoid signed overflow on malformed input, and then wraps once by the span max_dif. The encoder wraps each correction into [min_correction, max_correction], half the span, so the exact sum is at most one span outside [min, max] and one wrap is enough, but only if the sum is exact. When min and max are far apart the uint32_t sum itself wraps, the single wrap lands 2^32 away from the coded value, and every later prediction reads the wrong number. The decoder then either rejects the stream or decodes different values, for a stream its own encoder wrote. Doing the addition in int64_t keeps it exact for valid streams and still free of overflow for malformed ones. Sums that fit in int32 decode exactly as before. TestTexCoordPredictionThatWrapsRoundTrips builds six points with uint16 positions under constrained multi-parallelogram prediction and int32 texture coordinates under portable prediction, encodes them with EdgeBreaker at speeds 0/4 into a 911-byte stream and decodes it. Before this change the decode fails with "Failed to decode point attributes". After it, every decoded point carries a position and texture coordinate an input point carried. The mesh was found by fuzzing an independent implementation of the bitstream, which decodes the same stream.
igorvytyaz
previously approved these changes
Oct 9, 2026
igorvytyaz
approved these changes
Oct 9, 2026
Collaborator
|
Thank you for making this contribution to Draco. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PredictionSchemeWrapDecodingTransform::ComputeOriginalValue() adds the predicted value and the correction in uint32_t, to avoid signed overflow on malformed input, and then wraps once by the span max_dif. The encoder wraps each correction into [min_correction, max_correction], half the span, so the exact sum is at most one span outside [min, max] and one wrap is enough, but only if the sum is exact. When min and max are far apart the uint32_t sum itself wraps, the single wrap lands 2^32 away from the coded value, and every later prediction reads the wrong number. The decoder then either rejects the stream or decodes different values, for a stream its own encoder wrote.
Doing the addition in int64_t keeps it exact for valid streams and still free of overflow for malformed ones. Sums that fit in int32 decode exactly as before.
TestTexCoordPredictionThatWrapsRoundTrips builds six points with uint16 positions under constrained multi-parallelogram prediction and int32 texture coordinates under portable prediction, encodes them with EdgeBreaker at speeds 0/4 into a 911-byte stream and decodes it. Before this change the decode fails with "Failed to decode point attributes". After it, every decoded point carries a position and texture coordinate an input point carried. The mesh was found by fuzzing an independent implementation of the bitstream, which decodes the same stream.