From 8946da97858226a6603e9d20cdfdb929491aed42 Mon Sep 17 00:00:00 2001 From: xmgz Date: Sun, 8 Nov 2026 12:23:24 +0800 Subject: [PATCH] Bound sparse-array growth in JSInterpreter The index in a sparse assignment is evaluated from remote player JS and had no upper bound, so a single statement could size the local heap. Cap it at 1,000,000 elements and raise the interpreter exception above that. --- youtube_dl/jsinterp.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/youtube_dl/jsinterp.py b/youtube_dl/jsinterp.py index 7630e2099..a70f8adaa 100644 --- a/youtube_dl/jsinterp.py +++ b/youtube_dl/jsinterp.py @@ -318,6 +318,11 @@ _OPERATOR_RE = '|'.join(map(lambda x: re.escape(x[0]), _OPERATORS + _LOG_OPERATO _NAME_RE = r'[a-zA-Z_$][\w$]*' _MATCHING_PARENS = dict(zip(*zip('()', '{}', '[]'))) + +# Array indices come from untrusted remote JS (the player script). A single +# sparse assignment must not be able to size the local heap; the long-term fix +# is the dict-backed representation noted in the TODO below. +_MAX_SPARSE_ARRAY_LENGTH = 1000000 _QUOTES = '\'"/' _NESTED_BRACKETS = r'[^[\]]+(?:\[[^[\]]+(?:\[[^\]]+\])?\])?' @@ -1123,10 +1128,16 @@ class JSInterpreter(object): left_val = self._index(left_val, idx) if isinstance(idx, float): idx = int(idx) - if isinstance(left_val, list) and len(left_val) <= int_or_none(idx, default=-1): + if isinstance(left_val, list): # JS Array is a sparsely assignable list # TODO: handle extreme sparsity without memory bloat, eg using auxiliary dict - left_val.extend((idx - len(left_val) + 1) * [JS_Undefined]) + idx_n = int_or_none(idx, default=-1) + if idx_n >= _MAX_SPARSE_ARRAY_LENGTH: + raise self.Exception( + "Array index {0} exceeds the maximum sparse array length {1}" + .format(idx_n, _MAX_SPARSE_ARRAY_LENGTH), expr) + if len(left_val) <= idx_n: + left_val.extend((idx_n - len(left_val) + 1) * [JS_Undefined]) left_val[idx] = self._operator( m.group('op'), self._index(left_val, idx) if m.group('op') else None, m.group('expr'), expr, local_vars, allow_recursion)