Skip to content

decoder/dsdiff: reject overflowing chunk offsets - #2553

Merged
MaxKellermann merged 1 commit into
MusicPlayerDaemon:v0.24.xfrom
acts-1631:fix/dsdiff-chunk-overflow
Aug 10, 2026
Merged

decoder/dsdiff: reject overflowing chunk offsets#2553
MaxKellermann merged 1 commit into
MusicPlayerDaemon:v0.24.xfrom
acts-1631:fix/dsdiff-chunk-overflow

Conversation

@acts-1631

Copy link
Copy Markdown
Contributor

dsdiff_read_metadata() reads a 64-bit chunk size from the DFF file and
adds it to the current input offset. If this addition overflows, the
result can point backward. For seekable input, dsdlib_skip_to() then
seeks back to the same chunk header, causing metadata scans to loop
indefinitely. Playback follows the same parsing path and can spin until
stopped.

Use checked offset addition in dsdiff_read_metadata(),
dsdiff_read_prop(), and dsdiff_read_prop_snd(). Also reject overflow
when applying DSDIFF even-byte padding and in dsdlib_skip() relative
seeks. Absolute backward seeks remain supported because the tag reader
uses them intentionally.

Verified with a 28-byte DFF file whose unknown chunk size wraps the
offset from 28 back to 16. The checked calculation now rejects the
chunk.

uint64_t GetPaddedSize() const noexcept {
return (GetSize() + 1) & ~uint64_t(1);
[[nodiscard]]
bool GetPaddedSize(uint64_t &padded_size) const noexcept {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is horribly inelegant code and it's no longer obvious what this function really does. This needs API documentation. But probably it needs a rewrite to make it easier to understand. This whole PR looks like LLM slop.

@acts-1631
acts-1631 force-pushed the fix/dsdiff-chunk-overflow branch from 77093d6 to 7b5036d Compare August 2, 2026 13:06
Comment on lines +110 to +103
while (is.GetOffset() + sizeof(header) <= end_offset) {
while (is.GetOffset() < end_offset) {
if (end_offset - is.GetOffset() < sizeof(header))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would it be possible for an attacker to produce an overflow here? With a file of 2^64-8 bytes size?

Comment on lines +110 to +115
if ((chunk_size & 1) != 0) {
if (chunk_size == std::numeric_limits<uint64_t>::max())
return false;

++chunk_size;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is very obscure code. Previously, it was obvious that this was about padding, but you removed the code that mentioned padding. This is still too complicated!

The DSDIFF metadata parser adds an untrusted chunk size to the input
offset. An overflow can seek back to the chunk header and loop forever.

Reject overflowing end offsets before skipping unknown metadata chunks.
@acts-1631
acts-1631 force-pushed the fix/dsdiff-chunk-overflow branch from 7b5036d to f4f5946 Compare August 9, 2026 20:43
@MaxKellermann
MaxKellermann merged commit ad7d6a3 into MusicPlayerDaemon:v0.24.x Aug 10, 2026
10 checks passed
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.

2 participants