From efbef9cde8c8009ae6a2591384befc5b6be4d09e Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 27 Mar 2025 19:07:21 -0700 Subject: [PATCH 1/3] Rename rapidjson helper functions --- src/native/corehost/comhost/clsidmap.cpp | 4 ++-- src/native/corehost/fxr/sdk_resolver.cpp | 2 +- src/native/corehost/hostpolicy/deps_format.cpp | 4 ++-- src/native/corehost/json_parser.cpp | 14 ++++++++++---- src/native/corehost/json_parser.h | 4 ++-- src/native/corehost/runtime_config.cpp | 4 ++-- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/native/corehost/comhost/clsidmap.cpp b/src/native/corehost/comhost/clsidmap.cpp index af18abd8b2db92..218516c8da2569 100644 --- a/src/native/corehost/comhost/clsidmap.cpp +++ b/src/native/corehost/comhost/clsidmap.cpp @@ -100,7 +100,7 @@ namespace throw HResultException{ E_UNEXPECTED }; // This should never happen in Windows 7+ json_parser_t json; - if (!json.parse_raw_data(reinterpret_cast(data), size, _X(""))) + if (!json.parse_fully_trusted_raw_data(reinterpret_cast(data), size, _X(""))) { trace::error(_X("Embedded .clsidmap is invalid.\n %s"), json.get_error_message().c_str()); throw HResultException{ StatusCode::InvalidConfigFile }; @@ -178,7 +178,7 @@ namespace return {}; json_parser_t json; - if (!json.parse_file(map_file_name)) + if (!json.parse_fully_trusted_file(map_file_name)) { trace::error(_X("File .clsidmap [%s] is invalid.\n %s"), map_file_name.c_str(), json.get_error_message().c_str()); throw HResultException{ StatusCode::InvalidConfigFile }; diff --git a/src/native/corehost/fxr/sdk_resolver.cpp b/src/native/corehost/fxr/sdk_resolver.cpp index d3063a3db3d5a7..d1bab0d4a5bb2f 100644 --- a/src/native/corehost/fxr/sdk_resolver.cpp +++ b/src/native/corehost/fxr/sdk_resolver.cpp @@ -337,7 +337,7 @@ sdk_resolver::global_file_info sdk_resolver::parse_global_file(const pal::string // After we're done parsing `global_file_path`, none of its contents will be referenced // from the data private to json_parser_t; it's safe to declare it on the stack. json_parser_t json; - if (!json.parse_file(global_file_path)) + if (!json.parse_fully_trusted_file(global_file_path)) { ret.error_message = json.get_error_message(); ret.state = global_file_info::state::invalid_json; diff --git a/src/native/corehost/hostpolicy/deps_format.cpp b/src/native/corehost/hostpolicy/deps_format.cpp index 8c91c9fa84bbae..db0417db6e060d 100644 --- a/src/native/corehost/hostpolicy/deps_format.cpp +++ b/src/native/corehost/hostpolicy/deps_format.cpp @@ -94,7 +94,7 @@ deps_json_t::rid_fallback_graph_t deps_json_t::get_rid_fallback_graph(const pal: return rid_fallback_graph; json_parser_t json; - if (!json.parse_file(deps_path_local)) + if (!json.parse_fully_trusted_file(deps_path_local)) return rid_fallback_graph; populate_rid_fallback_graph(json.document(), rid_fallback_graph); @@ -591,7 +591,7 @@ void deps_json_t::load(bool is_framework_dependent, std::function Date: Tue, 24 Mar 2026 11:03:41 -0700 Subject: [PATCH 2/3] Remove in-situ parsing across all OSes --- src/native/corehost/json_parser.cpp | 30 +++++++++-------------------- src/native/corehost/json_parser.h | 2 +- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/native/corehost/json_parser.cpp b/src/native/corehost/json_parser.cpp index e2f57dde15db85..bdc4f12e5e7275 100644 --- a/src/native/corehost/json_parser.cpp +++ b/src/native/corehost/json_parser.cpp @@ -17,7 +17,7 @@ namespace { -void get_line_column_from_offset(const char* data, uint64_t size, size_t offset, int *line, int *column) +void get_line_column_from_offset(const char* data, size_t size, size_t offset, int *line, int *column) { assert(offset <= size); @@ -44,7 +44,7 @@ void get_line_column_from_offset(const char* data, uint64_t size, size_t offset, } // empty namespace -bool json_parser_t::parse_fully_trusted_raw_data(char* data, int64_t size, const pal::string_t& context) +bool json_parser_t::parse_fully_trusted_raw_data(char* data, size_t size, const pal::string_t& context) { // This code assumes that the provided data is fully trusted; that is, that no portion // of it has been provided by a hostile agent. @@ -52,15 +52,12 @@ bool json_parser_t::parse_fully_trusted_raw_data(char* data, int64_t size, const assert(data != nullptr); constexpr auto flags = rapidjson::ParseFlag::kParseStopWhenDoneFlag | rapidjson::ParseFlag::kParseCommentsFlag; -#ifdef _WIN32 - // Can't use in-situ parsing on Windows, as JSON data is encoded in - // UTF-8 and the host expects wide strings. m_document will store - // data in UTF-16 (with pal::char_t as the character type), but it - // has to know that data is encoded in UTF-8 to convert during parsing. - m_document.Parse>(data); -#else // _WIN32 - m_document.ParseInsitu(data); -#endif // _WIN32 + + // Can't use in-situ parsing, as RapidJson requires a null-terminated string, + // and the provided data may not be null-terminated. The input data is always + // expected to be UTF-8 encoded; m_document is initialized with the appropriate + // encoding type for the underlying OS (UTF-16 on Windows; UTF-8 elsewhere). + m_document.Parse>(data, size); if (m_document.HasParseError()) { @@ -97,9 +94,7 @@ bool json_parser_t::parse_fully_trusted_file(const pal::string_t& path) if (bundle::info_t::is_single_file_bundle()) { - // Due to in-situ parsing on Linux, - // * The json file is mapped as copy-on-write. - // * The mapping cannot be immediately released, and will be unmapped by the json_parser destructor. + // The mapping cannot be immediately released; it will be unmapped by the json_parser destructor. m_data = bundle::info_t::config_t::map(path, m_bundle_location); if (m_data != nullptr) @@ -110,14 +105,7 @@ bool json_parser_t::parse_fully_trusted_file(const pal::string_t& path) if (m_data == nullptr) { -#ifdef _WIN32 - // We can't use in-situ parsing on Windows, as JSON data is encoded in - // UTF-8 and the host expects wide strings. - // We do not need copy-on-write, so read-only mapping will be enough. m_data = (char*)pal::mmap_read(path, &m_size); -#else // _WIN32 - m_data = (char*)pal::mmap_copy_on_write(path, &m_size); -#endif // _WIN32 if (m_data == nullptr) { diff --git a/src/native/corehost/json_parser.h b/src/native/corehost/json_parser.h index c9dbe4c6480525..8bc2689395f1b5 100644 --- a/src/native/corehost/json_parser.h +++ b/src/native/corehost/json_parser.h @@ -37,7 +37,7 @@ class json_parser_t { const document_t& document() const { return m_document; } const pal::string_t& get_error_message() const { return m_parse_error; } - bool parse_fully_trusted_raw_data(char* data, int64_t size, const pal::string_t& context); + bool parse_fully_trusted_raw_data(char* data, size_t size, const pal::string_t& context); bool parse_fully_trusted_file(const pal::string_t& path); json_parser_t() From b877fd1e1e1fb8b5fcbe4912220d1a2f786d4524 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 24 Mar 2026 12:17:33 -0700 Subject: [PATCH 3/3] Fix off-by-one error in get_line_column_from_offset --- src/native/corehost/json_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/corehost/json_parser.cpp b/src/native/corehost/json_parser.cpp index bdc4f12e5e7275..e3004cf11c1b84 100644 --- a/src/native/corehost/json_parser.cpp +++ b/src/native/corehost/json_parser.cpp @@ -32,7 +32,7 @@ void get_line_column_from_offset(const char* data, size_t size, size_t offset, i (*line)++; *column = 1; } - else if (data[i] == '\r' && data[i + 1] == '\n') + else if (data[i] == '\r' && (i + 1) < offset && data[i + 1] == '\n') { (*line)++; *column = 1;