Skip to content

Commit

Permalink
Clear runfiles environment variables for bazel run
Browse files Browse the repository at this point in the history
When an environment variable such as `RUNFILES_DIR` is set in the client environment when a target using runfiles libraries is run via `bazel run`, the libraries can't look up the runfiles directory or manifest.

This is fixed by clearing the runfiles-related environment variables from the env in which the target is executed.

Fixes #17571

Closes #17690.

PiperOrigin-RevId: 516474822
Change-Id: Ia5201d4334b286b36ba2e476e850b98992ca0ffa
  • Loading branch information
fmeum authored and Copybara-Service committed Mar 14, 2023
1 parent 24470be commit bc83389
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/main/cpp/blaze.cc
Expand Up @@ -2151,6 +2151,12 @@ unsigned int BlazeServer::Communicate(
return blaze_exit_code::INTERNAL_ERROR;
}

// Clear environment variables before setting the requested ones so that
// users can still explicitly override the clearing.
for (const auto &variable_name : request.environment_variable_to_clear()) {
UnsetEnv(variable_name);
}

vector<string> argv(request.argv().begin(), request.argv().end());
for (const auto &variable : request.environment_variable()) {
SetEnv(variable.name(), variable.value());
Expand Down
Expand Up @@ -342,6 +342,7 @@ public void maybeReportSubcommand(Spawn spawn) {
showSubcommands.prettyPrintArgs,
spawn.getArguments(),
spawn.getEnvironment(),
/* environmentVariablesToClear= */ null,
getExecRoot().getPathString(),
spawn.getConfigurationChecksum(),
spawn.getExecutionPlatformLabelString());
Expand Down
Expand Up @@ -281,6 +281,7 @@ private void writeAction(ActionAnalysisMetadata action, PrintStream printStream)
.map(a -> escapeBytestringUtf8(a))
.collect(toImmutableList()),
/* environment= */ null,
/* environmentVariablesToClear= */ null,
/* cwd= */ null,
action.getOwner().getConfigurationChecksum(),
action.getExecutionPlatform() == null
Expand Down
Expand Up @@ -14,6 +14,7 @@

package com.google.devtools.build.lib.runtime.commands;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.nio.charset.StandardCharsets.ISO_8859_1;

import com.google.common.base.Joiner;
Expand Down Expand Up @@ -145,6 +146,17 @@ public static class RunOptions extends OptionsBase {

private static final FileType RUNFILES_MANIFEST = FileType.of(".runfiles_manifest");

private static final ImmutableList<String> ENV_VARIABLES_TO_CLEAR =
ImmutableList.of(
// These variables are all used by runfiles libraries to locate the runfiles directory or
// manifest and can cause incorrect behavior when set for the top-level binary run with
// bazel run.
"JAVA_RUNFILES",
"RUNFILES_DIR",
"RUNFILES_MANIFEST_FILE",
"RUNFILES_MANIFEST_ONLY",
"TEST_SRCDIR");

/** The test policy to determine the environment variables from when running tests */
private final TestPolicy testPolicy;

Expand Down Expand Up @@ -211,6 +223,7 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti
/* prettyPrintArgs= */ false,
runCommandLine.args,
runCommandLine.runEnvironment,
ENV_VARIABLES_TO_CLEAR,
runCommandLine.workingDir.getPathString(),
builtTargets.configuration.checksum(),
/* executionPlatformAsLabelString= */ null);
Expand Down Expand Up @@ -261,6 +274,7 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti
runCommandLine.workingDir,
runCommandLine.args,
runEnv.buildOrThrow(),
ENV_VARIABLES_TO_CLEAR,
builtTargets.configuration));
} catch (RunCommandException e) {
return e.result;
Expand Down Expand Up @@ -678,6 +692,7 @@ private static ExecRequest buildExecRequest(
Path workingDir,
ImmutableList<String> args,
ImmutableSortedMap<String, String> runEnv,
ImmutableList<String> runEnvToClear,
BuildConfigurationValue configuration)
throws RunCommandException {
ExecRequest.Builder execDescription =
Expand Down Expand Up @@ -729,6 +744,10 @@ private static ExecRequest buildExecRequest(
.setValue(ByteString.copyFrom(variable.getValue(), ISO_8859_1))
.build());
}
execDescription.addAllEnvironmentVariableToClear(
runEnvToClear.stream()
.map(s -> ByteString.copyFrom(s, ISO_8859_1))
.collect(toImmutableList()));
return execDescription.build();
}

Expand Down
Expand Up @@ -22,6 +22,7 @@
import java.io.File;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

Expand All @@ -41,6 +42,8 @@ private interface DescribeCommandImpl {
void describeCommandCwd(String cwd, StringBuilder message);
void describeCommandEnvPrefix(StringBuilder message, boolean isolated);
void describeCommandEnvVar(StringBuilder message, Map.Entry<String, String> entry);

void describeCommandUnsetEnvVar(StringBuilder message, String name);
/**
* Formats the command element and adds it to the message.
*
Expand Down Expand Up @@ -83,6 +86,12 @@ public void describeCommandEnvVar(StringBuilder message, Map.Entry<String, Strin
.append(ShellEscaper.escapeString(entry.getValue())).append(" \\\n ");
}

@Override
public void describeCommandUnsetEnvVar(StringBuilder message, String name) {
// Only the short form of --unset is supported on macOS.
message.append("-u ").append(ShellEscaper.escapeString(name)).append(" \\\n ");
}

@Override
public void describeCommandElement(
StringBuilder message, String commandElement, boolean isBinary) {
Expand Down Expand Up @@ -123,6 +132,11 @@ public void describeCommandEnvVar(StringBuilder message, Map.Entry<String, Strin
.append(entry.getValue()).append("\n ");
}

@Override
public void describeCommandUnsetEnvVar(StringBuilder message, String name) {
message.append("SET ").append(name).append('=').append("\n ");
}

@Override
public void describeCommandElement(
StringBuilder message, String commandElement, boolean isBinary) {
Expand Down Expand Up @@ -156,6 +170,7 @@ public static String describeCommand(
boolean prettyPrintArgs,
Collection<String> commandLineElements,
@Nullable Map<String, String> environment,
@Nullable List<String> environmentVariablesToClear,
@Nullable String cwd,
@Nullable String configurationChecksum,
@Nullable String executionPlatformAsLabelString) {
Expand Down Expand Up @@ -205,6 +220,12 @@ public static String describeCommand(
if (environment != null) {
describeCommandImpl.describeCommandEnvPrefix(
message, form != CommandDescriptionForm.COMPLETE_UNISOLATED);
if (environmentVariablesToClear != null) {
for (String name : Ordering.natural().sortedCopy(environmentVariablesToClear)) {
message.append(" ");
describeCommandImpl.describeCommandUnsetEnvVar(message, name);
}
}
// A map can never have two keys with the same value, so we only need to compare the keys.
Comparator<Map.Entry<String, String>> mapEntryComparator = comparingByKey();
for (Map.Entry<String, String> entry :
Expand Down Expand Up @@ -291,6 +312,7 @@ static String describeCommandFailure(
/* prettyPrintArgs= */ false,
commandLineElements,
env,
null,
cwd,
configurationChecksum,
executionPlatformAsLabelString));
Expand Down
Expand Up @@ -211,11 +211,12 @@ public String toString() {
// debugging.
return CommandFailureUtils.describeCommand(
CommandDescriptionForm.COMPLETE,
/*prettyPrintArgs=*/ false,
/* prettyPrintArgs= */ false,
args,
env,
/* environmentVariablesToClear= */ null,
execRoot.getPathString(),
/*configurationChecksum=*/ null,
/*executionPlatformAsLabelString=*/ null);
/* configurationChecksum= */ null,
/* executionPlatformAsLabelString= */ null);
}
}
1 change: 1 addition & 0 deletions src/main/protobuf/command_server.proto
Expand Up @@ -92,6 +92,7 @@ message ExecRequest {
bytes working_directory = 1;
repeated bytes argv = 2;
repeated EnvironmentVariable environment_variable = 3;
repeated bytes environment_variable_to_clear = 4;
}

// Contains metadata and result data for a command execution.
Expand Down
Expand Up @@ -15,6 +15,7 @@

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.cmdline.Label;
import java.util.Arrays;
Expand Down Expand Up @@ -210,6 +211,8 @@ public void describeCommandPrettyPrintArgs() throws Exception {
env.put("FOO", "foo");
env.put("PATH", "/usr/bin:/bin:/sbin");

ImmutableList<String> envToClear = ImmutableList.of("CLEAR", "THIS");

String cwd = "/my/working/directory";
PlatformInfo executionPlatform =
PlatformInfo.builder().setLabel(Label.parseCanonicalUnchecked("//platform:exec")).build();
Expand All @@ -219,6 +222,7 @@ public void describeCommandPrettyPrintArgs() throws Exception {
true,
Arrays.asList(args),
env,
envToClear,
cwd,
"cfg12345",
executionPlatform.label().toString());
Expand All @@ -227,6 +231,8 @@ public void describeCommandPrettyPrintArgs() throws Exception {
.isEqualTo(
"(cd /my/working/directory && \\\n"
+ " exec env - \\\n"
+ " -u CLEAR \\\n"
+ " -u THIS \\\n"
+ " FOO=foo \\\n"
+ " PATH=/usr/bin:/bin:/sbin \\\n"
+ " some_command \\\n"
Expand Down
47 changes: 47 additions & 0 deletions src/test/shell/bazel/run_test.sh
Expand Up @@ -125,4 +125,51 @@ eof
echo "$output" | grep --fixed-strings 'ExecuteProgram(C:\first_part second_part)' || fail "Expected error message to contain unquoted path"
}

function test_run_with_runfiles_env() {
mkdir -p b
cat > b/BUILD <<'EOF'
sh_binary(
name = "binary",
srcs = ["binary.sh"],
deps = ["@bazel_tools//tools/bash/runfiles"],
)
EOF
cat > b/binary.sh <<'EOF'
#!/usr/bin/env bash
# --- begin runfiles.bash initialization v3 ---
# Copy-pasted from the Bazel Bash runfiles library v3.
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v3 ---
own_path=$(rlocation main/b/binary.sh)
echo "own path: $own_path"
test -f "$own_path"
EOF
chmod +x b/binary.sh

bazel run //b:binary --script_path=script.bat &>"$TEST_log" \
|| fail "Script generation should succeed"

cat ./script.bat &>"$TEST_log"

# Make it so that the runfiles variables point to an incorrect but valid
# runfiles directory/manifest, simulating a left over one from a different
# test to which RUNFILES_DIR and RUNFILES_MANIFEST_FILE point in the client
# env.
BOGUS_RUNFILES_DIR="$(pwd)/bogus_runfiles/bazel_tools/tools/bash/runfiles"
mkdir -p "$BOGUS_RUNFILES_DIR"
touch "$BOGUS_RUNFILES_DIR/runfiles.bash"
BOGUS_RUNFILES_MANIFEST_FILE="$(pwd)/bogus_manifest"
echo "bazel_tools/tools/bash/runfiles/runfiles.bash bogus/path" > "$BOGUS_RUNFILES_MANIFEST_FILE"

RUNFILES_DIR="$BOGUS_RUNFILES_DIR" RUNFILES_MANIFEST_FILE="$BOGUS_RUNFILES_MANIFEST_FILE" \
./script.bat || fail "Run should succeed"
}

run_suite "run_under_tests"

0 comments on commit bc83389

Please sign in to comment.