Repository navigation
dmap: encode string tags with correct byte length - #2918
Open
peter-research wants to merge 1 commit into
Open
peter-research wants to merge 1 commit into
peter-research wants to merge 1 commit into
Conversation
tags.string_tag declared the DMAP length field as len(value), i.e. the number of characters, while the payload written is the UTF-8 encoding of the string. For any non-ASCII string the declared length is shorter than the actual data, producing a malformed TLV: a receiver parsing the tag reads a truncated, and in the middle of a multi-byte sequence, invalid UTF-8 payload, and any tag following it is misaligned. Encode once and use len(encoded) as the length field.
This branch has not been deployed
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.
Problem
tags.string_tag()inpyatv/protocols/dmap/tags.pybuilds a DMAP TLV whose 4-byte big-endian length field islen(value)— the number of characters — while the payload it writes isvalue.encode("utf-8"), i.e. a number of bytes.For any non-ASCII string those two numbers differ, so the tag is malformed:
DMAP is a plain TLV format (see the module docstring in
pyatv/protocols/dmap/parser.py): a 4-byte name, a 4-byte big-endian length, then exactly that many bytes of data. A receiver reading this tag gets the length from the header and therefore reads 6 of the 8 payload bytes, cutting the UTF-8 sequenceb'\xc3\xa5'in half:Because the length is wrong, the parser also resumes at the wrong offset, so any tag following the string is misparsed as garbage. On a real device this means non-ASCII metadata is either truncated or drops the rest of the payload.
This affects every place pyatv writes DMAP strings:
pyatv/support/rtsp.py—minm/asal/asarmetadata tags sent viaSET_PARAMETER(title, album, artist of whatever is streamed)pyatv/protocols/dmap/pairing.py—cmnmdevice name during pairingpyatv/protocols/dmap/__init__.py—cmbe/cmtecommand stringsThe bug is present since the initial commit (
cd7dac1b, 2017) and is not intentional design: no commit ever touched the length computation other than ablackreformat.Approach
Encode the string once and use
len(encoded)as the length field, so the declared length always matches the bytes on the wire:ASCII output is byte-for-byte identical to before, so only the previously-broken non-ASCII case changes.
Tests
Added
test_parse_non_ascii_stringtotests/protocols/dmap/test_parser.py, which round-trips two non-ASCII strings (Latin-1 supplement and CJK) throughstring_tag→parser.parse:Before the patch it fails with
KeyError: 'åss'(the parser resumed mid-string and read a bogus tag name). After the patch it passes.tests/protocols/dmap,tests/protocols/raopandtests/supportare green (537 passed, 5 skipped), andblack+ruffare clean.Risk
Low. The change is confined to one function and only affects the length field, which was already wrong for non-ASCII input. For ASCII input the emitted bytes are unchanged. No public API, no protocol behaviour and no existing test expectation is touched. The worst case if a receiver somehow depended on the buggy character count is unchanged behaviour for ASCII media, which is the overwhelming majority of traffic.