From 8bdb178d30a4c35673516d1690995dc2d5bbbb25 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 13 May 2026 22:55:38 +0200 Subject: [PATCH 1/3] only use dmlast for DML files distributed near the compiler --- py/dml/messages.py | 12 ++++++++++++ py/dml/toplevel.py | 7 +++++++ test/1.4/errors/T_WDMLAST.dml | 10 ++++++++++ test/1.4/errors/T_WDMLAST.dmlast | 0 test/tests.py | 2 ++ 5 files changed, 31 insertions(+) create mode 100644 test/1.4/errors/T_WDMLAST.dml create mode 100644 test/1.4/errors/T_WDMLAST.dmlast diff --git a/py/dml/messages.py b/py/dml/messages.py index 5a277e0c1..55358f3a6 100644 --- a/py/dml/messages.py +++ b/py/dml/messages.py @@ -2150,7 +2150,19 @@ def __init__(self, dmlfile): DMLWarning.__init__(self, SimpleSite(dmlfile + ":0"), dmlfile + "ast") +class WDMLAST(DMLWarning): + """Some files in the DML standard library are preparsed into files + with the `.dmlast` suffix. If such files are encountered + elsewhere, then they are ignored to avoid potential coherency + problems, and this warning is printed. + """ + fmt = "AST file for file outside the standard library: %s" + def __init__(self, dmlfile): + DMLWarning.__init__(self, SimpleSite(dmlfile + ":0"), + dmlfile + "ast") + class WWRNSTMT(DMLWarning): + """ The source code contained a statement "`warning;`", which causes a warning to be printed. diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 8e2e347ab..4fc64a302 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -319,6 +319,13 @@ def parse_dmlast_or_dml(dml_filename): # error in dml-builtins.dml, one accidentally edits the # copy in [host]/bin/dml/, instead of the one in the repo. report(WOLDAST(dml_filename)) + elif (Path(__file__).parent.parent.parent.parent + not in Path(ast_filename).parents): + # The .dmlast file mechanism is meant only to speed up the + # parsing of the standard library. Using it elsewhere would + # mean that parser updates can cause confusing errors, and the + # speed gains are small, so refuse to do that. + report(WDMLAST(dml_filename)) else: file_info, pragmas, parsedata = load_dmlast(ast_filename) if file_info.name is None: diff --git a/test/1.4/errors/T_WDMLAST.dml b/test/1.4/errors/T_WDMLAST.dml new file mode 100644 index 000000000..c425aff4c --- /dev/null +++ b/test/1.4/errors/T_WDMLAST.dml @@ -0,0 +1,10 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// COMPILE-ONLY +/// WARNING WDMLAST T_WDMLAST.dml diff --git a/test/1.4/errors/T_WDMLAST.dmlast b/test/1.4/errors/T_WDMLAST.dmlast new file mode 100644 index 000000000..e69de29bb diff --git a/test/tests.py b/test/tests.py index 922ac4ab1..bc2246db3 100644 --- a/test/tests.py +++ b/test/tests.py @@ -1889,6 +1889,8 @@ def test(self): # Don't bother. 'test/1.2/misc/T_dos_newline.dml', # empty + 'test/1.4/errors/T_WDMLAST.dmlast', + # empty 'test/SUITEINFO', # data files 'test/XFAIL', From 61619efc23689b6a51d20f0d7c3492e037e1ce05 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Mon, 18 May 2026 09:13:27 +0200 Subject: [PATCH 2/3] Harden unpickling of dmlast files `pickle.loads` is unsafe for untrusted data, and although dmlast files are only used on trusted data, it is hard for the reader to deduce this so it is easier to use the safe unpickler idiom instead of suppressing security warnings. --- py/dml/toplevel.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 4fc64a302..9e119c8c2 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -279,10 +279,22 @@ def parse_file(dml_filename): ast = parse(contents, file_info, dml_filename, version) return ast +class ASTUnpickler(pickle.Unpickler): + _safe_classes = { + ('dml.ast', 'AST'), + ('dml.logging', 'DumpableSite'), + ('dml.logging', 'FileInfo')} + + def find_class(self, module, name): + if (module, name) not in self._safe_classes: + raise pickle.UnpicklingError('broken dmlast data') + return super().find_class(module, name) + def load_dmlast(ast_filename): '''Return a previously compiled AST, or None''' try: - return pickle.loads(bz2.BZ2File(ast_filename).read()) # nosec + with bz2.BZ2File(ast_filename) as f: + return ASTUnpickler(f).load() except Exception as e: raise ICE(SimpleSite(ast_filename), "Failed to load AST from %r: %s" From 3bbacb12d07b0151242752dc253d92ab3d803442 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Mon, 18 May 2026 09:13:27 +0200 Subject: [PATCH 3/3] Don't resolve symlinks in filenames shown in sites Symlink resolution caused the standard DML lib of fake pkgs in pkgs/ to appear as `/linux64/bin/dml` --- py/dml/toplevel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 9e119c8c2..9dd95b2d2 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -446,8 +446,7 @@ def parse_main_file(inputfilename, explicit_import_path): deps.setdefault(path, set()).add(importfile) - path = str(Path(path).resolve()) - normalized = os.path.normcase(path) + normalized = os.path.normcase(str(Path(path).resolve())) if normalized in imported: # Already imported if importfile not in imported[normalized]: