• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/files/scoped_file.h"
6 
7 #include <dlfcn.h>
8 
9 #include <algorithm>
10 #include <array>
11 #include <atomic>
12 
13 #include "base/compiler_specific.h"
14 #include "base/debug/stack_trace.h"
15 #include "base/immediate_crash.h"
16 #include "base/logging.h"
17 
18 namespace {
19 
20 // We want to avoid any kind of allocations in our close() implementation, so we
21 // use a fixed-size table. Given our common FD limits and the preference for new
22 // FD allocations to use the lowest available descriptor, this should be
23 // sufficient to guard most FD lifetimes. The worst case scenario if someone
24 // attempts to own a higher FD is that we don't track it.
25 const int kMaxTrackedFds = 4096;
26 
27 std::atomic_bool g_is_ownership_enforced{false};
28 std::array<std::atomic_bool, kMaxTrackedFds> g_is_fd_owned;
29 
CrashOnFdOwnershipViolation()30 NOINLINE void CrashOnFdOwnershipViolation() {
31   RAW_LOG(ERROR, "Crashing due to FD ownership violation:\n");
32   base::debug::StackTrace().Print();
33   base::ImmediateCrash();
34 }
35 
CanTrack(int fd)36 bool CanTrack(int fd) {
37   return fd >= 0 && fd < kMaxTrackedFds;
38 }
39 
UpdateAndCheckFdOwnership(int fd,bool owned)40 void UpdateAndCheckFdOwnership(int fd, bool owned) {
41   if (CanTrack(fd) &&
42       g_is_fd_owned[static_cast<size_t>(fd)].exchange(owned) == owned &&
43       g_is_ownership_enforced) {
44     CrashOnFdOwnershipViolation();
45   }
46 }
47 
48 }  // namespace
49 
50 namespace base {
51 namespace internal {
52 
53 // static
Acquire(const ScopedFD & owner,int fd)54 void ScopedFDCloseTraits::Acquire(const ScopedFD& owner, int fd) {
55   UpdateAndCheckFdOwnership(fd, /*owned=*/true);
56 }
57 
58 // static
Release(const ScopedFD & owner,int fd)59 void ScopedFDCloseTraits::Release(const ScopedFD& owner, int fd) {
60   UpdateAndCheckFdOwnership(fd, /*owned=*/false);
61 }
62 
63 }  // namespace internal
64 
65 namespace subtle {
66 
67 #if !defined(COMPONENT_BUILD)
EnableFDOwnershipEnforcement(bool enabled)68 void EnableFDOwnershipEnforcement(bool enabled) {
69   g_is_ownership_enforced = enabled;
70 }
71 #endif  // !defined(COMPONENT_BUILD)
72 
ResetFDOwnership()73 void ResetFDOwnership() {
74   std::fill(g_is_fd_owned.begin(), g_is_fd_owned.end(), false);
75 }
76 
77 }  // namespace subtle
78 
IsFDOwned(int fd)79 bool IsFDOwned(int fd) {
80   return CanTrack(fd) && g_is_fd_owned[static_cast<size_t>(fd)];
81 }
82 
83 }  // namespace base
84 
85 #if !defined(COMPONENT_BUILD)
86 using LibcCloseFuncPtr = int (*)(int);
87 
88 // Load the libc close symbol to forward to from the close wrapper.
LoadCloseSymbol()89 LibcCloseFuncPtr LoadCloseSymbol() {
90 #if defined(THREAD_SANITIZER)
91   // If TSAN is enabled use __interceptor___close first to make sure the TSAN
92   // wrapper gets called.
93   return reinterpret_cast<LibcCloseFuncPtr>(
94       dlsym(RTLD_DEFAULT, "__interceptor___close"));
95 #else
96   return reinterpret_cast<LibcCloseFuncPtr>(dlsym(RTLD_NEXT, "close"));
97 #endif
98 }
99 
100 extern "C" {
101 
102 NO_SANITIZE("cfi-icall")
close(int fd)103 __attribute__((visibility("default"), noinline)) int close(int fd) {
104   static LibcCloseFuncPtr libc_close = LoadCloseSymbol();
105   if (base::IsFDOwned(fd) && g_is_ownership_enforced)
106     CrashOnFdOwnershipViolation();
107   if (libc_close == nullptr) {
108     RAW_LOG(ERROR, "close symbol missing\n");
109     base::ImmediateCrash();
110   }
111   return libc_close(fd);
112 }
113 
114 }  // extern "C"
115 #endif  // !defined(COMPONENT_BUILD)
116