Repository: dotnet/runtime
File: src/native/corehost/hostpolicy/deps_resolver.cpp (C++)

Code before:
            breadcrumb->insert(entry.library_name);
        }

        // Ignore placeholders
        if (utils::ends_with(entry.asset.relative_path, _X("/_._"), false))
        {
            return true;
        }

        trace::info(_X("Processing TPA for deps entry [%s, %s, %s, local_path: %s] with fx level: %d"),
            entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str(), entry.asset.local_path.empty() ? _X("<not_set>") : entry.asset.local_path.c_str(), fx_level);

        pal::string_t resolved_path;

        name_to_resolved_asset_map_t::iterator existing = items.find(entry.asset.name);
        if (existing == items.end())
        {
            probe_result_t probe_result = probe_deps_entry(entry, deps_dir, fx_level, &resolved_path);
            if (probe_result != probe_result_t::not_found)
            {
                // Assemblies loaded directly from the bundle are not added to the TPA list.
                // The runtime directly probes the bundle-manifest using a host-callback.
                if (probe_result != probe_result_t::bundled)
                {
                    add_tpa_asset(entry.asset.name, &entry.asset, std::move(resolved_path), &items);
                }

                return true;
            }

            return report_missing_assembly_in_manifest(entry, ignore_missing_assemblies);
        }
        else
        {
            // Verify the extension is the same as the previous verified entry
            if (get_deps_filename(entry.asset.relative_path) != get_filename(existing->second.resolved_path))
            {
                trace::error(
                    DuplicateAssemblyWithDifferentExtensionMessage,
                    entry.deps_file.c_str(),
                    entry.library_name.c_str(),
                    entry.library_version.c_str(),
                    entry.asset.relative_path.c_str(),
                    existing->second.resolved_path.c_str());

                return false;
            }

            deps_resolved_asset_t* existing_entry = &existing->second;

            // If deps entry is same or newer than existing, then see if it should be replaced

Review comment (suggestion):
deps_resolved_asset_t now stores a raw pointer to the deps_asset_t passed into add_tpa_asset. In resolve_tpa_list, this assumes the referenced deps_entry_t (and its asset) outlives the items map. That’s not true for the additional-deps path: the loop below uses `auto additional_deps_entries = additional_deps->get_entries(...)` (copy) and iterates by value, so &entry.asset can dangle, leading to UAF during later version comparisons/logging. Fix by iterating over the underlying deps_json_t entries by reference (e.g., `const auto& additional_deps_entries = ...; for (const auto& entry : additional_deps_entries)`), or store just the version values in deps_resolved_asset_t instead of a pointer.

Code after:
            breadcrumb->insert(entry.library_name + _X(",") + entry.library_version);
            breadcrumb->insert(entry.library_name);
        }

        // Ignore placeholders
        if (utils::ends_with(entry.asset.relative_path, _X("/_._"), false))
        {
            return true;
        }

        trace::info(_X("Processing TPA for deps entry [%s, %s, %s, local_path: %s] with fx level: %d"),
            entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str(), entry.asset.local_path.empty() ? _X("<not_set>") : entry.asset.local_path.c_str(), fx_level);

        pal::string_t resolved_path;

        name_to_resolved_asset_map_t::iterator existing = items.find(entry.asset.name);
        if (existing == items.end())
        {
            probe_result_t probe_result = probe_deps_entry(entry, deps_dir, fx_level, &resolved_path);
            if (probe_result != probe_result_t::not_found)
            {
                // Assemblies loaded directly from the bundle are not added to the TPA list.
                // The runtime directly probes the bundle-manifest using a host-callback.
                if (probe_result != probe_result_t::bundled)
                {
                    add_tpa_asset(entry.asset.name, &entry.asset, std::move(resolved_path), &items);
                }

                return true;
            }

            return report_missing_assembly_in_manifest(entry, ignore_missing_assemblies);
        }
        else
        {
            // Verify the extension is the same as the previous verified entry
            if (get_deps_filename(entry.asset.relative_path) != get_filename(existing->second.resolved_path))
            {
                trace::error(
                    DuplicateAssemblyWithDifferentExtensionMessage,
                    entry.deps_file.c_str(),
                    entry.library_name.c_str(),
                    entry.library_version.c_str(),
                    entry.asset.relative_path.c_str(),
                    existing->second.resolved_path.c_str());

                return false;
            }

            deps_resolved_asset_t* existing_entry = &existing->second;