mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Update symlink traversal limit when resolving interpreter path.
When execveat is called on an interpreter script, the symlink count for resolving the script path should be separate from the count for resolving the the corresponding interpreter. An ELOOP error should not occur if we do not hit the symlink limit along any individual path, even if the total number of symlinks encountered exceeds the limit. Closes #574 PiperOrigin-RevId: 277358474
This commit is contained in:
@@ -644,6 +644,8 @@ func loadELF(ctx context.Context, args LoadArgs) (loadedELF, arch.Context, error
|
||||
// resolved, the interpreter should still be resolved if it is
|
||||
// a symlink.
|
||||
args.ResolveFinal = true
|
||||
// Refresh the traversal limit.
|
||||
*args.RemainingTraversals = linux.MaxSymlinkTraversals
|
||||
args.Filename = bin.interpreter
|
||||
d, i, err := openPath(ctx, args)
|
||||
if err != nil {
|
||||
|
||||
@@ -293,6 +293,8 @@ func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, arch.Context
|
||||
ctx.Infof("Error loading interpreter script: %v", err)
|
||||
return loadedELF{}, nil, nil, nil, err
|
||||
}
|
||||
// Refresh the traversal limit for the interpreter.
|
||||
*args.RemainingTraversals = linux.MaxSymlinkTraversals
|
||||
default:
|
||||
ctx.Infof("Unknown magic: %v", hdr)
|
||||
return loadedELF{}, nil, nil, nil, syserror.ENOEXEC
|
||||
|
||||
@@ -533,6 +533,47 @@ TEST(ExecTest, CloexecEventfd) {
|
||||
W_EXITCODE(0, 0), "");
|
||||
}
|
||||
|
||||
constexpr int kLinuxMaxSymlinks = 40;
|
||||
|
||||
TEST(ExecTest, SymlinkLimitExceeded) {
|
||||
std::string path = WorkloadPath(kBasicWorkload);
|
||||
|
||||
// Hold onto TempPath objects so they are not destructed prematurely.
|
||||
std::vector<TempPath> symlinks;
|
||||
for (int i = 0; i < kLinuxMaxSymlinks + 1; i++) {
|
||||
symlinks.push_back(
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo("/tmp", path)));
|
||||
path = symlinks[i].path();
|
||||
}
|
||||
|
||||
int execve_errno;
|
||||
ASSERT_NO_ERRNO_AND_VALUE(
|
||||
ForkAndExec(path, {path}, {}, /*child=*/nullptr, &execve_errno));
|
||||
EXPECT_EQ(execve_errno, ELOOP);
|
||||
}
|
||||
|
||||
TEST(ExecTest, SymlinkLimitRefreshedForInterpreter) {
|
||||
std::string tmp_dir = "/tmp";
|
||||
std::string interpreter_path = "/bin/echo";
|
||||
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
|
||||
tmp_dir, absl::StrCat("#!", interpreter_path), 0755));
|
||||
std::string script_path = script.path();
|
||||
|
||||
// Hold onto TempPath objects so they are not destructed prematurely.
|
||||
std::vector<TempPath> interpreter_symlinks;
|
||||
std::vector<TempPath> script_symlinks;
|
||||
for (int i = 0; i < kLinuxMaxSymlinks; i++) {
|
||||
interpreter_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE(
|
||||
TempPath::CreateSymlinkTo(tmp_dir, interpreter_path)));
|
||||
interpreter_path = interpreter_symlinks[i].path();
|
||||
script_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE(
|
||||
TempPath::CreateSymlinkTo(tmp_dir, script_path)));
|
||||
script_path = script_symlinks[i].path();
|
||||
}
|
||||
|
||||
CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), "");
|
||||
}
|
||||
|
||||
TEST(ExecveatTest, BasicWithFDCWD) {
|
||||
std::string path = WorkloadPath(kBasicWorkload);
|
||||
CheckExecveat(AT_FDCWD, path, {path}, {}, /*flags=*/0, ArgEnvExitStatus(0, 0),
|
||||
|
||||
Reference in New Issue
Block a user