Skip to content

Commit bcbf2f0

Browse files
committed
crash-decode: do not eval() linker script memory expressions
The crash-decode helper evaluated arithmetic from a user-supplied linker script with eval(), so a crafted crash bundle could run arbitrary Python on the analyst's machine. Parse the expressions with a restricted evaluator that only accepts integer literals and basic arithmetic operators. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1 parent 3f7738d commit bcbf2f0

1 file changed

Lines changed: 57 additions & 2 deletions

File tree

scripts/sof-crash-decode.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,61 @@
3535
import os
3636
import json
3737
import shlex
38+
import ast
39+
import operator
40+
41+
42+
# Largest shift we accept: linker addresses/sizes fit well under 2**64, so a
43+
# bigger shift is nonsensical and only serves to build a huge integer.
44+
_MAX_SHIFT = 64
45+
46+
47+
def _trunc_div(a, b):
48+
# truncate toward zero (C semantics); Python's // floors instead
49+
q = abs(a) // abs(b)
50+
return -q if (a < 0) != (b < 0) else q
51+
52+
53+
def _lshift(a, b):
54+
# reject absurd shift counts to avoid building a massive integer
55+
if b < 0 or b > _MAX_SHIFT:
56+
raise ValueError("shift count out of range")
57+
return a << b
58+
59+
60+
# Operators allowed when evaluating arithmetic from an untrusted linker script.
61+
_SAFE_OPS = {
62+
ast.Add: operator.add, ast.Sub: operator.sub,
63+
ast.Mult: operator.mul, ast.Div: _trunc_div,
64+
ast.Mod: operator.mod, ast.LShift: _lshift,
65+
ast.RShift: operator.rshift, ast.BitOr: operator.or_,
66+
ast.BitAnd: operator.and_, ast.BitXor: operator.xor,
67+
ast.USub: operator.neg, ast.UAdd: operator.pos,
68+
}
69+
70+
71+
def safe_eval_int(expr):
72+
"""Evaluate an integer arithmetic expression without executing code.
73+
74+
A crash bundle's linker.cmd is attacker-controllable, so its MEMORY
75+
expressions must never be passed to eval(). Only integer literals and
76+
basic arithmetic operators are accepted; anything else raises ValueError.
77+
"""
78+
def _eval(node):
79+
if isinstance(node, ast.Expression):
80+
return _eval(node.body)
81+
if isinstance(node, ast.Constant):
82+
# reject bool (a subclass of int) and everything non-integer
83+
if type(node.value) is int:
84+
return node.value
85+
raise ValueError("non-integer constant")
86+
if isinstance(node, ast.BinOp) and type(node.op) in _SAFE_OPS:
87+
return _SAFE_OPS[type(node.op)](_eval(node.left), _eval(node.right))
88+
if isinstance(node, ast.UnaryOp) and type(node.op) in _SAFE_OPS:
89+
return _SAFE_OPS[type(node.op)](_eval(node.operand))
90+
raise ValueError("unsupported expression")
91+
92+
return _eval(ast.parse(expr, mode='eval'))
3893

3994
XTENSA_EXCCAUSE = {
4095
0: "No Error (or IllegalInstruction)",
@@ -151,8 +206,8 @@ def parse_linker_cmd(filepath):
151206
org_expr = m_org.group(1).strip()
152207
len_expr = m_len.group(1).strip()
153208
try:
154-
org_val = eval(org_expr)
155-
len_val = eval(len_expr)
209+
org_val = safe_eval_int(org_expr)
210+
len_val = safe_eval_int(len_expr)
156211
# Ignore debug regions
157212
if not (name.startswith('.debug') or name.startswith('.stab')):
158213
regions.append({'name': name, 'start': org_val, 'end': org_val + len_val})

0 commit comments

Comments
 (0)