diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc index b867c081b100a4..22ed10bbfef107 100644 --- a/src/main/tools/linux-sandbox-pid1.cc +++ b/src/main/tools/linux-sandbox-pid1.cc @@ -130,9 +130,19 @@ static int CreateTarget(const char* path, bool is_directory) { } struct stat sb; - // If the path already exists... - - if (stat(path, &sb) == 0) { + // Use lstat() instead of stat() to avoid following symlinks. + // If a parent component under sandbox_root is a pre-seeded symlink, + // stat() would follow it and subsequent mkdir()/link() calls would + // operate outside sandbox_root. Using lstat() detects and rejects + // symlinks, preventing writes to arbitrary host paths. + // See https://github.com/bazelbuild/bazel/issues/28515 + + if (lstat(path, &sb) == 0) { + if (S_ISLNK(sb.st_mode)) { + // Reject symlinks: following them could escape the sandbox root. + errno = ELOOP; + return -1; + } if (is_directory && S_ISDIR(sb.st_mode)) { // and it's a directory and supposed to be a directory, we're done here. return 0; @@ -145,7 +155,7 @@ static int CreateTarget(const char* path, bool is_directory) { return -1; } } else { - // If stat failed because of any error other than "the path does not exist", + // If lstat failed because of any error other than "the path does not exist", // this is an error. if (errno != ENOENT) { return -1;