Skip to content

Commit ef5be0d

Browse files
committed
fix: isolate host tools from target runtime env
1 parent 4c9905f commit ef5be0d

7 files changed

Lines changed: 111 additions & 8 deletions

File tree

src/platform/env.cppm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private:
4040

4141
std::string path_list_separator();
4242
std::string runtime_library_path_key();
43+
std::string host_tool_runtime_library_path_key();
4344
std::string prepend_path_list(std::string_view key,
4445
std::span<const std::filesystem::path> dirs);
4546

@@ -123,6 +124,16 @@ std::string runtime_library_path_key() {
123124
#endif
124125
}
125126

127+
std::string host_tool_runtime_library_path_key() {
128+
#if defined(__APPLE__)
129+
return "DYLD_LIBRARY_PATH";
130+
#elif defined(__linux__)
131+
return "LD_LIBRARY_PATH";
132+
#else
133+
return "";
134+
#endif
135+
}
136+
126137
std::string prepend_path_list(std::string_view key,
127138
std::span<const std::filesystem::path> dirs) {
128139
if (key.empty() || dirs.empty()) return "";

src/platform/linux.cppm

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ export namespace mcpp::platform::linux_ {
1919
std::string build_ld_library_path_prefix(
2020
const std::vector<std::filesystem::path>& dirs);
2121

22+
// Build an LD_LIBRARY_PATH shell prefix for toolchain host processes.
23+
// Unlike build_ld_library_path_prefix(), this does not append inherited
24+
// LD_LIBRARY_PATH, which may contain target-program runtime directories.
25+
std::string build_clean_ld_library_path_prefix(
26+
const std::vector<std::filesystem::path>& dirs);
27+
2228
// Return Linux toolchain runtime library directories.
2329
std::vector<std::filesystem::path>
2430
runtime_lib_dirs(const std::filesystem::path& toolchain_root);
@@ -29,16 +35,24 @@ runtime_lib_dirs(const std::filesystem::path& toolchain_root);
2935

3036
namespace mcpp::platform::linux_ {
3137

32-
std::string build_ld_library_path_prefix(
33-
const std::vector<std::filesystem::path>& dirs)
34-
{
35-
#if defined(__linux__)
36-
if (dirs.empty()) return "";
38+
namespace {
39+
40+
std::string join_dirs(const std::vector<std::filesystem::path>& dirs) {
3741
std::string joined;
3842
for (auto& d : dirs) {
3943
if (!joined.empty()) joined += ':';
4044
joined += d.string();
4145
}
46+
return joined;
47+
}
48+
49+
} // namespace
50+
51+
std::string build_ld_library_path_prefix(
52+
const std::vector<std::filesystem::path>& dirs) {
53+
#if defined(__linux__)
54+
if (dirs.empty()) return "";
55+
auto joined = join_dirs(dirs);
4256
return std::format("env LD_LIBRARY_PATH={}${{LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}} ",
4357
mcpp::platform::shell::quote(joined));
4458
#else
@@ -47,6 +61,19 @@ std::string build_ld_library_path_prefix(
4761
#endif
4862
}
4963

64+
std::string build_clean_ld_library_path_prefix(
65+
const std::vector<std::filesystem::path>& dirs) {
66+
#if defined(__linux__)
67+
if (dirs.empty()) return "";
68+
auto joined = join_dirs(dirs);
69+
return std::format("env LD_LIBRARY_PATH={} ",
70+
mcpp::platform::shell::quote(joined));
71+
#else
72+
(void)dirs;
73+
return "";
74+
#endif
75+
}
76+
5077
std::vector<std::filesystem::path>
5178
runtime_lib_dirs(const std::filesystem::path& toolchain_root) {
5279
std::vector<std::filesystem::path> dirs;

src/platform/process.cppm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module;
3030
export module mcpp.platform.process;
3131

3232
import std;
33+
import mcpp.platform.env;
3334

3435
export namespace mcpp::platform::process {
3536

@@ -42,6 +43,11 @@ struct RunResult {
4243
// On POSIX, stdin is automatically redirected from /dev/null.
4344
RunResult capture(std::string_view command);
4445

46+
// Run a host tool while clearing target runtime library search variables.
47+
// This prevents target/program LD_LIBRARY_PATH from poisoning system tools
48+
// such as sha256sum, compiler probes, env, or the shell itself.
49+
RunResult capture_host_tool(std::string_view command);
50+
4551
// Run `command` with extra environment variables (additive).
4652
// Windows: _putenv_s (mutates calling process env).
4753
// POSIX: prefixes command with VAR=val tokens (no mutation).
@@ -126,6 +132,14 @@ RunResult capture(std::string_view command) {
126132
return result;
127133
}
128134

135+
RunResult capture_host_tool(std::string_view command) {
136+
auto key = mcpp::platform::env::host_tool_runtime_library_path_key();
137+
std::optional<mcpp::platform::env::ScopedEnv> runtime_env;
138+
if (!key.empty())
139+
runtime_env.emplace(key, std::nullopt);
140+
return capture(command);
141+
}
142+
129143
RunResult capture_with_env(
130144
std::string_view command,
131145
const std::vector<std::pair<std::string, std::string>>& env)

src/pm/publisher.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ std::string sha256_of_file(const std::filesystem::path& file) {
206206
if (!std::filesystem::exists(file)) return {};
207207
auto cmd = std::format("sha256sum {} 2>/dev/null",
208208
mcpp::platform::shell::quote(file.string()));
209-
auto r = mcpp::platform::process::capture(cmd);
209+
auto r = mcpp::platform::process::capture_host_tool(cmd);
210210
if (r.exit_code != 0) return {};
211211
// sha256sum format: "<64-hex> <filename>\n"
212212
auto sp = r.output.find(' ');

src/toolchain/probe.cppm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ std::string join_colon_paths(const std::vector<std::filesystem::path>& dirs) {
8888
}
8989

9090
std::string env_prefix_for_dirs(const std::vector<std::filesystem::path>& dirs) {
91-
return mcpp::platform::linux_::build_ld_library_path_prefix(dirs);
91+
return mcpp::platform::linux_::build_clean_ld_library_path_prefix(dirs);
9292
}
9393

9494
} // namespace
9595

9696
std::expected<std::string, DetectError> run_capture(const std::string& cmd) {
97-
auto r = mcpp::platform::process::capture(cmd);
97+
auto r = mcpp::platform::process::capture_host_tool(cmd);
9898
if (r.exit_code != 0 && r.output.empty()) {
9999
return std::unexpected(DetectError{std::format("failed to execute: {}", cmd)});
100100
}

tests/unit/test_toolchain_detect.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <gtest/gtest.h>
22

33
import std;
4+
import mcpp.platform.env;
45
import mcpp.toolchain.detect;
56
import mcpp.toolchain.probe;
67

@@ -52,6 +53,14 @@ esac
5253
return clang;
5354
}
5455

56+
std::filesystem::path make_hostile_ld_dir() {
57+
auto dir = std::filesystem::temp_directory_path()
58+
/ std::format("mcpp_hostile_ld_{}", std::random_device{}());
59+
std::filesystem::create_directories(dir);
60+
std::ofstream(dir / "libc.so.6").close();
61+
return dir;
62+
}
63+
5564
} // namespace
5665

5766
#if !defined(_WIN32)
@@ -70,6 +79,22 @@ TEST(ToolchainDetect, ClangVersionOutputIsNotMisclassifiedByGccPaths) {
7079
}
7180
#endif // !defined(_WIN32)
7281

82+
#if defined(__linux__)
83+
TEST(ToolchainDetect, IgnoresTargetRuntimeLibraryPathDuringProbe) {
84+
auto clang = make_fake_clang();
85+
TempDirGuard cleanup_clang{clang.parent_path()};
86+
auto hostile = make_hostile_ld_dir();
87+
TempDirGuard cleanup_ld{hostile};
88+
89+
mcpp::platform::env::ScopedEnv ld("LD_LIBRARY_PATH", hostile.string());
90+
91+
auto tc = detect(clang);
92+
ASSERT_TRUE(tc.has_value()) << tc.error().message;
93+
EXPECT_EQ(tc->compiler, CompilerId::Clang);
94+
EXPECT_EQ(tc->targetTriple, "x86_64-unknown-linux-gnu");
95+
}
96+
#endif // defined(__linux__)
97+
7398
// ─── normalize_driver_output: path-free semantic identity ─────────────
7499
//
75100
// Background: the toolchain fingerprint used to hash the compiler binary

tests/unit/test_xpkg_emit.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import std;
44
import mcpp.manifest;
55
import mcpp.modgraph.graph;
6+
import mcpp.platform.env;
67
import mcpp.publish.xpkg_emit;
78

89
using namespace mcpp::publish;
@@ -120,6 +121,31 @@ TEST(XpkgEmit, Sha256OfFile) {
120121
}
121122
#endif // !defined(_WIN32)
122123

124+
#if defined(__linux__)
125+
TEST(XpkgEmit, Sha256OfFileIgnoresTargetRuntimeLibraryPath) {
126+
using namespace mcpp::publish;
127+
128+
auto hostile = std::filesystem::temp_directory_path()
129+
/ std::format("mcpp_hostile_ld_{}", std::random_device{}());
130+
std::filesystem::create_directories(hostile);
131+
{ std::ofstream(hostile / "libc.so.6").close(); }
132+
133+
auto p = std::filesystem::temp_directory_path()
134+
/ std::format("mcpp_unit_sha_hostile_{}", std::random_device{}());
135+
{ std::ofstream(p).close(); }
136+
137+
{
138+
mcpp::platform::env::ScopedEnv ld("LD_LIBRARY_PATH", hostile.string());
139+
EXPECT_EQ(sha256_of_file(p),
140+
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
141+
}
142+
143+
std::error_code ec;
144+
std::filesystem::remove(p, ec);
145+
std::filesystem::remove_all(hostile, ec);
146+
}
147+
#endif // defined(__linux__)
148+
123149
TEST(XpkgEmit, LongBracketSequenceInValueIsHarmless) {
124150
// We emit `"..."` strings, not `[[...]]`, so a literal `]=]` in
125151
// user data can't terminate the literal early. Just make sure it

0 commit comments

Comments
 (0)