Index: content/common/sandbox_linux.cc |
diff --git a/content/common/sandbox_linux.cc b/content/common/sandbox_linux.cc |
index 2ce96b67d4514842c6a5d291a98f0f759751ca0f..78ddcccc2cf7a6db4713f807167ad9a3569f73bd 100644 |
--- a/content/common/sandbox_linux.cc |
+++ b/content/common/sandbox_linux.cc |
@@ -2,6 +2,7 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <dirent.h> |
#include <fcntl.h> |
#include <sys/resource.h> |
#include <sys/stat.h> |
@@ -60,6 +61,54 @@ bool IsRunningTSAN() { |
#endif |
} |
+#if !defined(NDEBUG) |
jln (very slow on Chromium)
2013/10/25 19:49:52
Maybe do DEBUG_NOTREACHED() instead which does CHE
Mostyn Bramley-Moore
2013/10/26 07:53:44
Removed this in favour of DIRDeleter.
|
+// For when using DCHECK would be incorrect, since it can be enabled in |
+// non official release mode. |
jln (very slow on Chromium)
2013/10/25 19:49:52
Just explain what it does, the "for when using DCH
Mostyn Bramley-Moore
2013/10/26 07:53:44
Removed this in favour of DIRDeleter.
|
+#define REAL_DEBUG_CHECK(x) CHECK(x) |
+#else |
+#define REAL_DEBUG_CHECK(x) |
+#endif // !defined(NDEBUG) |
+ |
+class OpenDirDeleter { |
jln (very slow on Chromium)
2013/10/25 19:49:52
Sorry, what I meant by creating an OpenDirDeleter(
Mostyn Bramley-Moore
2013/10/26 07:53:44
I hadn't noticed this version of scoped_ptr- looks
|
+ public: |
+ |
+ OpenDirDeleter() : dir_(NULL) {} |
+ |
+ ~OpenDirDeleter() { |
+ if (dir_) { |
+ int result = closedir(dir_); |
+ (void)result; |
+ REAL_DEBUG_CHECK(result == 0); |
+ } |
+ } |
+ |
+ // Just like the real fdopendir except it returns bool instead |
+ // of a directory stream. Returns false if this method on this object |
+ // was successfully called previously, or if the requested dir could not |
+ // be opened. Assumes ownership of fd. |
+ bool fdopendir(int fd) { |
+ if (dir_) { |
+ return false; |
+ } |
+ |
+ // The "real" fdopendir takes ownership of fd here. |
+ dir_ = ::fdopendir(fd); |
+ if (!dir_) { |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ // Call readdir_r on the directory stream descriptor and return the result. |
+ int readdir_r(struct dirent *entry, struct dirent **result) { |
+ return ::readdir_r(dir_, entry, result); |
+ } |
+ |
+ private: |
+ |
jln (very slow on Chromium)
2013/10/25 19:49:52
FYI (since this class may disappear): always add D
Mostyn Bramley-Moore
2013/10/25 20:41:15
Will do, thanks for the tip.
|
+ DIR *dir_; |
+}; |
+ |
} // namespace |
namespace content { |
@@ -143,6 +192,12 @@ bool LinuxSandbox::InitializeSandbox() { |
return false; |
} |
+ if (linux_sandbox->HasOpenDirectories()) { |
+ LOG(FATAL) << "InitializeSandbox() called after unexpected directries " |
jln (very slow on Chromium)
2013/10/25 19:49:52
nit: directories
Mostyn Bramley-Moore
2013/10/26 07:53:44
Done.
|
+ "have been opened- the setuid sandbox may be at risk, if " |
+ "the BPF sandbox is not running."; |
+ } |
+ |
// Attempt to limit the future size of the address space of the process. |
linux_sandbox->LimitAddressSpace(process_type); |
@@ -208,6 +263,111 @@ bool LinuxSandbox::IsSingleThreaded() const { |
return task_stat.st_nlink == 3; |
} |
+bool LinuxSandbox::HasOpenDirectories() const { |
+ // Note: this function won't catch the following cases: |
+ // 1) proc_fd_ is valid and /proc is opened elsewhere a second time (but not |
+ // closed). |
+ // 2) /proc/self/fd is opened elsewhere a second time (but not closed). |
+ |
+ // If /proc is already open, store the device/inode pair that uniquely |
+ // identifies it. |
+ dev_t proc_device = 0; |
+ ino_t proc_inode = 0; |
+ |
+ // To store the device/inode pair that uniquely identifies /proc/self/fd. |
+ dev_t proc_self_fd_device = 0; |
+ ino_t proc_self_fd_inode = 0; |
+ |
+ int proc_self_fd = 0; |
+ |
+ struct stat s; |
jln (very slow on Chromium)
2013/10/25 19:49:52
Please, scope your variables as much as possible.
Mostyn Bramley-Moore
2013/10/26 07:53:44
Done.
|
+ int stat_res; |
+ |
+ if (proc_fd_ >= 0) { |
jln (very slow on Chromium)
2013/10/25 19:49:52
You're handling the case where we don't have proc_
Mostyn Bramley-Moore
2013/10/25 20:41:15
I could leave a "the code after this point can ass
jln (very slow on Chromium)
2013/10/26 01:15:15
We should check that in debug mode we have proc_fd
Mostyn Bramley-Moore
2013/10/26 07:53:44
Rather than create a local proc_fd and having to b
|
+ proc_self_fd = openat(proc_fd_, "self/fd", O_RDONLY|O_DIRECTORY); |
+ REAL_DEBUG_CHECK(proc_self_fd != -1); |
jln (very slow on Chromium)
2013/10/25 19:49:52
This should be a normal CHECK.
Mostyn Bramley-Moore
2013/10/26 07:53:44
Done.
|
+ if (proc_self_fd == -1) { |
+ // We can't read the list of open file descriptors, so guess false. |
+ return false; |
+ } |
+ |
+ stat_res = fstat(proc_fd_, &s); |
+ REAL_DEBUG_CHECK(stat_res == 0); |
jln (very slow on Chromium)
2013/10/25 19:49:52
This could be a normal check
Mostyn Bramley-Moore
2013/10/26 07:53:44
Removed this, since we don't need to stat /proc.
|
+ if (stat_res != 0) { |
+ // If we continue, we'll get a false positive, so guess false. |
+ return false; |
+ } |
+ proc_device = s.st_dev; |
+ proc_inode = s.st_ino; |
+ |
+ } else { |
+ proc_self_fd = open("/proc/self/fd", O_RDONLY|O_DIRECTORY); |
+ REAL_DEBUG_CHECK(proc_self_fd != -1); |
jln (very slow on Chromium)
2013/10/25 19:49:52
This should be a normal CHECK
Mostyn Bramley-Moore
2013/10/26 07:53:44
Removed this case, since we return early if proc_f
|
+ if (proc_self_fd == -1) { |
+ // We can't read the list of open file descriptors, so guess false. |
+ return false; |
+ } |
+ } |
+ |
+ stat_res = fstat(proc_self_fd, &s); |
+ REAL_DEBUG_CHECK(stat_res == 0); |
jln (very slow on Chromium)
2013/10/25 19:49:52
This should be a normal CHECK
Mostyn Bramley-Moore
2013/10/26 07:53:44
Removed this, since we don't need to stat /proc/se
|
+ if (stat_res != 0) { |
+ // If we continue we'll get a false positive, so guess false. |
+ return false; |
+ } |
+ proc_self_fd_device = s.st_dev; |
+ proc_self_fd_inode = s.st_ino; |
+ |
+ // We now have unique identifiers for /proc/self/fd and possibly /proc, |
+ // let's see if there are any other directories open. |
jln (very slow on Chromium)
2013/10/25 19:49:52
Are you hitting issue with implementation details
Mostyn Bramley-Moore
2013/10/25 20:41:15
No, I'm just trying to be more thorough and cover
jln (very slow on Chromium)
2013/10/26 01:15:15
No, we absolutely need to fail in this case, that'
Mostyn Bramley-Moore
2013/10/26 07:53:44
Ah right, I misunderstood this part of the dup man
|
+ |
+ scoped_ptr<OpenDirDeleter> odd(new OpenDirDeleter()); |
jln (very slow on Chromium)
2013/10/25 19:49:52
As explained above, I was thinking of a custom del
Mostyn Bramley-Moore
2013/10/26 07:53:44
Done.
|
+ |
+ // Ownership of proc_self_fd is transferred here, it must not be closed |
+ // or modified afterwards except by odd. |
+ if (!odd->fdopendir(proc_self_fd)) { |
+ REAL_DEBUG_CHECK(false); |
+ // We can't read the list of open file descriptors, so guess false. |
+ return false; |
+ } |
+ |
+ struct dirent e, *de; |
+ while (!odd->readdir_r(&e, &de) && de) { |
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) |
+ continue; |
+ |
+ // Stat the file pointed to by the current file descriptor. |
+ // It's OK to use proc_self_fd here, fstatat won't modify it. |
+ stat_res = fstatat(proc_self_fd, e.d_name, &s, 0); |
+ REAL_DEBUG_CHECK(stat_res == 0); |
+ if (stat_res != 0) { |
+ // We failed to check this file descriptor but maybe a later one will |
+ // succeed(?), so let's continue rather than guessing false immediately. |
+ continue; |
+ } |
+ |
+ if (S_ISDIR(s.st_mode)) { |
+ // Check if this directory is one of the managed directories that are |
+ // OK to have open. |
+ |
+ if (proc_fd_ != -1 && proc_device |
+ && proc_device == s.st_dev && proc_inode == s.st_ino) { |
+ continue; // Skip over /proc. |
+ } |
+ |
+ if (proc_self_fd_device == s.st_dev && proc_self_fd_inode == s.st_ino) { |
+ continue; // Skip over /proc/self/fd. |
+ } |
+ |
+ // We found an open dir that wasn't /proc or /proc/self/fd. |
+ return true; |
+ } |
+ } |
+ |
+ // No open unmanaged directories found. \o/ |
+ return false; |
+} |
+ |
bool LinuxSandbox::seccomp_bpf_started() const { |
return seccomp_bpf_started_; |
} |