Add check to pivot_root that ensures the new root is underneath the old root.

Reported-by: syzbot+bb796623970e2eef9a5f@syzkaller.appspotmail.com
PiperOrigin-RevId: 589957215
This commit is contained in:
Lucas Manning
2023-12-11 14:53:47 -08:00
committed by gVisor bot
parent a047cde5e6
commit eaee2b213b
2 changed files with 39 additions and 0 deletions
+4
View File
@@ -1131,6 +1131,10 @@ func (vfs *VirtualFilesystem) PivotRoot(ctx context.Context, creds *auth.Credent
if !vfs.isPathReachable(ctx, newRoot, putOld) {
return newRoot, oldRoot, linuxerr.EINVAL
}
// the new root must be at or underneath the current root.
if !vfs.isPathReachable(ctx, oldRoot, newRoot) {
return newRoot, oldRoot, linuxerr.EINVAL
}
// The current root directory must be a mountpoint
// (in the case it has been chrooted).
if oldRoot.mount.root != oldRoot.dentry {
+35
View File
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
@@ -35,6 +36,7 @@
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/linux_capability_util.h"
#include "test/util/logging.h"
#include "test/util/mount_util.h"
#include "test/util/multiprocess_util.h"
@@ -47,6 +49,8 @@ namespace testing {
namespace {
constexpr char kTmpfs[] = "tmpfs";
TEST(PivotRootTest, Success) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
@@ -514,6 +518,37 @@ TEST(PivotRootTest, OnSharedPutOldMountpoint) {
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
TEST(PivotRootTest, UnreachableNewRootFails) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
TempPath outside_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
ASSERT_THAT(mount("", outside_root.path().c_str(), kTmpfs, 0, ""),
SyscallSucceeds());
TempPath root =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outside_root.path()));
ASSERT_THAT(mount("", root.path().c_str(), kTmpfs, 0, ""), SyscallSucceeds());
TempPath new_root =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path()));
ASSERT_THAT(mount("", new_root.path().c_str(), kTmpfs, 0, ""),
SyscallSucceeds());
const std::string new_root_path =
absl::StrCat("/", Basename(new_root.path()));
TempPath put_old =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(new_root.path()));
const std::string put_old_path =
JoinPath(new_root_path, Basename(put_old.path()));
ASSERT_THAT(chdir(JoinPath(root.path(), "..").c_str()), SyscallSucceeds());
const std::function<void()> rest = [&] {
TEST_CHECK_SUCCESS(chroot(root.path().c_str()));
// "." references the directory outside the chroot.
TEST_CHECK_ERRNO(syscall(__NR_pivot_root, ".", put_old_path.c_str()),
EINVAL);
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
} // namespace
} // namespace testing