demux/ebml: interpret zero-length elements as their default value

An EBML element is permitted to have a zero-length data payload. It's
then interpreted as the element's RFC declared default value, or zero.

Before this change all zero-length elements were initialized to empty or
rejected in case of uint.

Fixes: #18351
This commit is contained in:
Kacper Michajłow committed 2026-08-14 23:24:50 +02:00
1 parent 52bb166f30
commit e167836802
3 files changed
+77 -37

No files matched your search

+48 -31
View File
@@ -28,13 +28,13 @@ from math import ldexp
elements_ebml = ( elements_ebml = (
"EBML, 1a45dfa3, sub", ( "EBML, 1a45dfa3, sub", (
"EBMLVersion, 4286, uint", "EBMLVersion, 4286, uint, default=1",
"EBMLReadVersion, 42f7, uint", "EBMLReadVersion, 42f7, uint, default=1",
"EBMLMaxIDLength, 42f2, uint", "EBMLMaxIDLength, 42f2, uint, default=4",
"EBMLMaxSizeLength, 42f3, uint", "EBMLMaxSizeLength, 42f3, uint, default=8",
"DocType, 4282, str", "DocType, 4282, str",
"DocTypeVersion, 4287, uint", "DocTypeVersion, 4287, uint, default=1",
"DocTypeReadVersion, 4285, uint", "DocTypeReadVersion, 4285, uint, default=1",
), ),
"CRC32, bf, binary", "CRC32, bf, binary",
@@ -55,7 +55,7 @@ elements_matroska = (
"SegmentUID, 73a4, binary, length=16", "SegmentUID, 73a4, binary, length=16",
"PrevUID, 3cb923, binary, length=16", "PrevUID, 3cb923, binary, length=16",
"NextUID, 3eb923, binary, length=16", "NextUID, 3eb923, binary, length=16",
"TimecodeScale, 2ad7b1, uint", "TimecodeScale, 2ad7b1, uint, default=1000000",
"DateUTC, 4461, sint", "DateUTC, 4461, sint",
"Title, 7ba9, str", "Title, 7ba9, str",
"MuxingApp, 4d80, str", "MuxingApp, 4d80, str",
@@ -72,7 +72,7 @@ elements_matroska = (
"DiscardPadding, 75A2, sint", "DiscardPadding, 75A2, sint",
"BlockAdditions, 75A1, sub", ( "BlockAdditions, 75A1, sub", (
"BlockMore*, A6, sub", ( "BlockMore*, A6, sub", (
"BlockAddID, EE, uint", "BlockAddID, EE, uint, default=1",
"BlockAdditional, A5, binary", "BlockAdditional, A5, binary",
), ),
), ),
@@ -85,34 +85,34 @@ elements_matroska = (
"TrackNumber, d7, uint", "TrackNumber, d7, uint",
"TrackUID, 73c5, uint", "TrackUID, 73c5, uint",
"TrackType, 83, uint", "TrackType, 83, uint",
"FlagEnabled, b9, uint", "FlagEnabled, b9, uint, default=1",
"FlagDefault, 88, uint", "FlagDefault, 88, uint, default=1",
"FlagForced, 55aa, uint", "FlagForced, 55aa, uint",
"FlagHearingImpaired, 55ab, uint", "FlagHearingImpaired, 55ab, uint",
"FlagVisualImpaired, 55ac, uint", "FlagVisualImpaired, 55ac, uint",
"FlagOriginal, 55ae, uint", "FlagOriginal, 55ae, uint",
"FlagCommentary, 55af, uint", "FlagCommentary, 55af, uint",
"FlagLacing, 9c, uint", "FlagLacing, 9c, uint, default=1",
"MinCache, 6de7, uint", "MinCache, 6de7, uint",
"MaxCache, 6df8, uint", "MaxCache, 6df8, uint",
"DefaultDuration, 23e383, uint", "DefaultDuration, 23e383, uint",
"TrackTimecodeScale, 23314f, float", "TrackTimecodeScale, 23314f, float, default=1",
"MaxBlockAdditionID, 55ee, uint", "MaxBlockAdditionID, 55ee, uint",
"Name, 536e, str", "Name, 536e, str",
"Language, 22b59c, str", "Language, 22b59c, str, default=eng",
"LanguageBCP47, 22b59d, str", "LanguageBCP47, 22b59d, str",
"CodecID, 86, str", "CodecID, 86, str",
"CodecPrivate, 63a2, binary", "CodecPrivate, 63a2, binary",
"CodecName, 258688, str", "CodecName, 258688, str",
"CodecDecodeAll, aa, uint", "CodecDecodeAll, aa, uint, default=1",
"CodecDelay, 56aa, uint", "CodecDelay, 56aa, uint",
"SeekPreRoll, 56bb, uint", "SeekPreRoll, 56bb, uint",
"Video, e0, sub", ( "Video, e0, sub", (
"FlagInterlaced, 9a, uint", "FlagInterlaced, 9a, uint",
"PixelWidth, b0, uint", "PixelWidth, b0, uint",
"PixelHeight, ba, uint", "PixelHeight, ba, uint",
"DisplayWidth, 54b0, uint", "DisplayWidth, 54b0, uint, default=context",
"DisplayHeight, 54ba, uint", "DisplayHeight, 54ba, uint, default=context",
"DisplayUnit, 54b2, uint", "DisplayUnit, 54b2, uint",
"PixelCropTop, 54bb, uint", "PixelCropTop, 54bb, uint",
"PixelCropLeft, 54cc, uint", "PixelCropLeft, 54cc, uint",
@@ -122,7 +122,7 @@ elements_matroska = (
"ColourSpace, 2eb524, binary, length=4", "ColourSpace, 2eb524, binary, length=4",
"StereoMode, 53b8, uint", "StereoMode, 53b8, uint",
"Colour, 55b0, sub", ( "Colour, 55b0, sub", (
"MatrixCoefficients, 55B1, uint", "MatrixCoefficients, 55B1, uint, default=2",
"BitsPerChannel, 55B2, uint", "BitsPerChannel, 55B2, uint",
"ChromaSubsamplingHorz, 55B3, uint", "ChromaSubsamplingHorz, 55B3, uint",
"ChromaSubsamplingVert, 55B4, uint", "ChromaSubsamplingVert, 55B4, uint",
@@ -131,8 +131,8 @@ elements_matroska = (
"ChromaSitingHorz, 55B7, uint", "ChromaSitingHorz, 55B7, uint",
"ChromaSitingVert, 55B8, uint", "ChromaSitingVert, 55B8, uint",
"Range, 55B9, uint", "Range, 55B9, uint",
"TransferCharacteristics, 55BA, uint", "TransferCharacteristics, 55BA, uint, default=2",
"Primaries, 55BB, uint", "Primaries, 55BB, uint, default=2",
"MaxCLL, 55BC, uint", "MaxCLL, 55BC, uint",
"MaxFALL, 55BD, uint", "MaxFALL, 55BD, uint",
"MasteringMetadata, 55D0, sub", ( "MasteringMetadata, 55D0, sub", (
@@ -157,15 +157,15 @@ elements_matroska = (
), ),
), ),
"Audio, e1, sub", ( "Audio, e1, sub", (
"SamplingFrequency, b5, float", "SamplingFrequency, b5, float, default=8000",
"OutputSamplingFrequency, 78b5, float", "OutputSamplingFrequency, 78b5, float, default=context",
"Channels, 9f, uint", "Channels, 9f, uint, default=1",
"BitDepth, 6264, uint", "BitDepth, 6264, uint",
), ),
"ContentEncodings, 6d80, sub", ( "ContentEncodings, 6d80, sub", (
"ContentEncoding*, 6240, sub", ( "ContentEncoding*, 6240, sub", (
"ContentEncodingOrder, 5031, uint", "ContentEncodingOrder, 5031, uint",
"ContentEncodingScope, 5032, uint", "ContentEncodingScope, 5032, uint, default=1",
"ContentEncodingType, 5033, uint", "ContentEncodingType, 5033, uint",
"ContentCompression, 5034, sub", ( "ContentCompression, 5034, sub", (
"ContentCompAlgo, 4254, uint", "ContentCompAlgo, 4254, uint",
@@ -215,12 +215,12 @@ elements_matroska = (
"ChapterTimeStart, 91, uint", "ChapterTimeStart, 91, uint",
"ChapterTimeEnd, 92, uint", "ChapterTimeEnd, 92, uint",
"ChapterFlagHidden, 98, uint", "ChapterFlagHidden, 98, uint",
"ChapterFlagEnabled, 4598, uint", "ChapterFlagEnabled, 4598, uint, default=1",
"ChapterSegmentUID, 6e67, binary, length=16", "ChapterSegmentUID, 6e67, binary, length=16",
"ChapterSegmentEditionUID, 6ebc, uint", "ChapterSegmentEditionUID, 6ebc, uint",
"ChapterDisplay*, 80, sub", ( "ChapterDisplay*, 80, sub", (
"ChapString, 85, str", "ChapString, 85, str",
"ChapLanguage*, 437c, str", "ChapLanguage*, 437c, str, default=eng",
"ChapLanguageBCP47*, 437d, str", "ChapLanguageBCP47*, 437d, str",
"ChapCountry*, 437e, str", "ChapCountry*, 437e, str",
), ),
@@ -230,7 +230,7 @@ elements_matroska = (
"Tags*, 1254c367, sub", ( "Tags*, 1254c367, sub", (
"Tag*, 7373, sub", ( "Tag*, 7373, sub", (
"Targets, 63c0, sub", ( "Targets, 63c0, sub", (
"TargetTypeValue, 68ca, uint", "TargetTypeValue, 68ca, uint, default=50",
"TargetType, 63ca, str", "TargetType, 63ca, str",
"TargetTrackUID, 63c5, uint", "TargetTrackUID, 63c5, uint",
"TargetEditionUID, 63c9, uint", "TargetEditionUID, 63c9, uint",
@@ -239,10 +239,10 @@ elements_matroska = (
), ),
"SimpleTag*, 67c8, sub", ( "SimpleTag*, 67c8, sub", (
"TagName, 45a3, str", "TagName, 45a3, str",
"TagLanguage, 447a, str", "TagLanguage, 447a, str, default=und",
"TagLanguageBCP47, 447b, str", "TagLanguageBCP47, 447b, str",
"TagString, 4487, str", "TagString, 4487, str",
"TagDefault, 4484, uint", "TagDefault, 4484, uint, default=1",
), ),
), ),
), ),
@@ -266,7 +266,8 @@ def camelcase_to_words(name):
class MatroskaElement: class MatroskaElement:
def __init__(self, name, elid, valtype, namespace, length=None): def __init__(self, name, elid, valtype, namespace, length=None,
default=None):
self.name = name self.name = name
self.definename = f"{namespace}_ID_{name.upper()}" self.definename = f"{namespace}_ID_{name.upper()}"
self.fieldname = camelcase_to_words(name) self.fieldname = camelcase_to_words(name)
@@ -274,6 +275,7 @@ class MatroskaElement:
self.elid = elid self.elid = elid
self.valtype = valtype self.valtype = valtype
self.length = int(length or "0") self.length = int(length or "0")
self.default = default
if valtype == "sub": if valtype == "sub":
self.ebmltype = "EBML_TYPE_SUBELEMENTS" self.ebmltype = "EBML_TYPE_SUBELEMENTS"
self.valname = "struct " + self.structname self.valname = "struct " + self.structname
@@ -292,6 +294,21 @@ class MatroskaElement:
self.subelements = subelements self.subelements = subelements
self.subids = {x[0].elid for x in subelements} self.subids = {x[0].elid for x in subelements}
def default_initializer(self):
if self.default is None:
return "false, {0}"
if self.default == "context":
return "true, {0}"
if self.valtype == "uint":
return f"false, {{.u = {int(self.default)}}}"
if self.valtype == "sint":
return f"false, {{.i = {int(self.default)}}}"
if self.valtype == "float":
return f"false, {{.f = {float(self.default)}}}"
if self.valtype == "str":
return f'false, {{.s = "{self.default}"}}'
raise SyntaxError("Default not supported for type " + self.valtype)
elementd: dict[str, MatroskaElement] = {} elementd: dict[str, MatroskaElement] = {}
elementlist: list[MatroskaElement] = [] elementlist: list[MatroskaElement] = []
def parse_elems(elements, namespace): def parse_elems(elements, namespace):
@@ -302,7 +319,7 @@ def parse_elems(elements, namespace):
attrs = {} attrs = {}
for extra in rest: for extra in rest:
key, sep, value = extra.partition("=") key, sep, value = extra.partition("=")
if not sep or key not in ("length",): if not sep or key not in ("length", "default"):
raise SyntaxError("Invalid element attribute " + extra) raise SyntaxError("Invalid element attribute " + extra)
attrs[key] = value attrs[key] = value
hexid = hexid.lower() hexid = hexid.lower()
@@ -371,7 +388,7 @@ def generate_c_definitions(out):
printf(out, "#undef N") printf(out, "#undef N")
else: else:
printf(out, f'E("{el.name}", {el.fieldname}, {el.ebmltype}, ' printf(out, f'E("{el.name}", {el.fieldname}, {el.ebmltype}, '
f'{el.length})') f'{el.length}, {el.default_initializer()})')
def read(s, length): def read(s, length):
t = s.read(length) t = s.read(length)
+22 -6
View File
@@ -210,8 +210,11 @@ int ebml_resync_cluster(struct mp_log *log, stream_t *s)
static_assert(sizeof(((struct ebml_elem_desc *)0)->defval) == sizeof(((struct ebml_elem_desc *)0)->defval.u),
"defval.u must cover the entire union for {0} initialization");
#define EVALARGS(F, ...) F(__VA_ARGS__) #define EVALARGS(F, ...) F(__VA_ARGS__)
#define E(str, N, type, length) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type, length }; #define E(str, N, type, length, context, ...) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, type, length, .context_default = context, .defval = __VA_ARGS__ };
#define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){ #define E_SN(str, count, N) const struct ebml_elem_desc ebml_ ## N ## _desc = { str, EBML_TYPE_SUBELEMENTS, sizeof(struct ebml_ ## N), count, (const struct ebml_field_desc[]){
#define E_S(str, count) EVALARGS(E_SN, str, count, N) #define E_S(str, count) EVALARGS(E_SN, str, count, N)
#define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc}, #define FN(id, name, multiple, N) { id, multiple, offsetof(struct ebml_ ## N, name), offsetof(struct ebml_ ## N, n_ ## name), &ebml_##name##_desc},
@@ -510,11 +513,13 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
else \ else \
subelptr = (fieldtype *) fieldptr subelptr = (fieldtype *) fieldptr
GETPTR(uintptr, uint64_t); GETPTR(uintptr, uint64_t);
if (length < 1 || length > 8) { if (length > 8) {
MP_ERR(ctx, "uint invalid length %"PRIu64"\n", length); MP_ERR(ctx, "uint invalid length %"PRIu64"\n", length);
goto error; goto error;
} }
*uintptr = ebml_parse_uint(data, length); if (!length && ed->context_default)
goto skip;
*uintptr = length ? ebml_parse_uint(data, length) : ed->defval.u;
MP_TRACE(ctx, "uint %"PRIu64"\n", *uintptr); MP_TRACE(ctx, "uint %"PRIu64"\n", *uintptr);
break; break;
@@ -525,7 +530,9 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
MP_ERR(ctx, "sint invalid length %"PRIu64"\n", length); MP_ERR(ctx, "sint invalid length %"PRIu64"\n", length);
goto error; goto error;
} }
*sintptr = ebml_parse_sint(data, length); if (!length && ed->context_default)
goto skip;
*sintptr = length ? ebml_parse_sint(data, length) : ed->defval.i;
MP_TRACE(ctx, "sint %"PRId64"\n", *sintptr); MP_TRACE(ctx, "sint %"PRId64"\n", *sintptr);
break; break;
@@ -536,7 +543,9 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
MP_ERR(ctx, "float invalid length %"PRIu64"\n", length); MP_ERR(ctx, "float invalid length %"PRIu64"\n", length);
goto error; goto error;
} }
*floatptr = ebml_parse_float(data, length); if (!length && ed->context_default)
goto skip;
*floatptr = length ? ebml_parse_float(data, length) : ed->defval.f;
MP_DBG(ctx, "float %f\n", *floatptr); MP_DBG(ctx, "float %f\n", *floatptr);
break; break;
@@ -547,7 +556,13 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
} }
char **strptr; char **strptr;
GETPTR(strptr, char *); GETPTR(strptr, char *);
*strptr = talloc_strndup(ctx->talloc_ctx, data, length); if (!length && ed->context_default)
goto skip;
if (!length && ed->defval.s) {
*strptr = talloc_strdup(ctx->talloc_ctx, ed->defval.s);
} else {
*strptr = talloc_strndup(ctx->talloc_ctx, data, length);
}
MP_TRACE(ctx, "string \"%s\"\n", *strptr); MP_TRACE(ctx, "string \"%s\"\n", *strptr);
break; break;
@@ -583,6 +598,7 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
} }
*countptr += 1; *countptr += 1;
error: error:
skip:
data += length; data += length;
} }
} }
+7
View File
@@ -54,6 +54,13 @@ struct ebml_elem_desc {
int size; int size;
int field_count; int field_count;
const struct ebml_field_desc *fields; const struct ebml_field_desc *fields;
bool context_default; // default derives from other elements
union {
uint64_t u;
int64_t i;
double f;
const char *s;
} defval; // default value of a zero-length element
}; };
struct ebml_parse_ctx { struct ebml_parse_ctx {