From d7549b4b36b88ccc78fc2851be14aad35c6011c1 Mon Sep 17 00:00:00 2001 From: yuxing12 Date: Mon, 8 Jun 2026 00:25:13 -0600 Subject: [PATCH] Fix path traversal in xir_util dump_bin (CWE-22) dump_subgraph1() built output file names by concatenating dirname with the subgraph name and reg_id keys taken directly from the deserialized .xmodel protobuf, without sanitization. A crafted .xmodel whose name / reg_id contains "../" sequences (or an absolute path) could make xir_util dump_bin write attacker-controlled data outside the intended output directory, allowing arbitrary file overwrite. Sanitize the file component in mk_file_name() with one unified rule on every platform: replace path separators ('/', '\'), the NTFS stream separator (':') and the characters Windows forbids (<>"|?*) with '_', so every dumped file stays inside dirname. On Windows, reserved device names (CON, NUL, COM1..9, LPT1..9, ...) are additionally prefixed. Co-authored-by: Cursor --- tools/cmd_dump_code.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tools/cmd_dump_code.cpp b/tools/cmd_dump_code.cpp index 6e217ad..681c30a 100644 --- a/tools/cmd_dump_code.cpp +++ b/tools/cmd_dump_code.cpp @@ -20,11 +20,13 @@ #include #include +#include #include #include #include #include #include +#include #include using namespace std; @@ -53,7 +55,30 @@ static void create_parent_path(const std::string& path) { } static std::string mk_file_name(const std::string& dirname, const std::string& file) { - return dirname + "/" + file; + std::string safe; + safe.reserve(file.size()); + for (char c : file) { + const bool unsafe = c == '/' || c == '\\' || c == ':' || c == '<' || + c == '>' || c == '"' || c == '|' || c == '?' || + c == '*'; + safe.push_back(unsafe ? '_' : c); + } + CHECK(!safe.empty() && safe != "." && safe != "..") + << "invalid output filename derived from xmodel: " << file; +#ifdef _WIN32 + { + auto stem = safe.substr(0, safe.find('.')); + for (auto& ch : stem) ch = (char)::toupper((unsigned char)ch); + static const std::set kReserved = { + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", + "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", + "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}; + if (kReserved.count(stem)) { + safe = "_" + safe; + } + } +#endif + return dirname + "/" + safe; } static void dump_subgraph1(const xir::Subgraph* sg,