xref: /aosp_15_r20/external/cronet/base/files/scoped_file_linux.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 #include "base/strings/string_piece.h"
18 
19 namespace {
20 
21 // We want to avoid any kind of allocations in our close() implementation, so we
22 // use a fixed-size table. Given our common FD limits and the preference for new
23 // FD allocations to use the lowest available descriptor, this should be
24 // sufficient to guard most FD lifetimes. The worst case scenario if someone
25 // attempts to own a higher FD is that we don't track it.
26 const int kMaxTrackedFds = 4096;
27 
28 std::atomic_bool g_is_ownership_enforced{false};
29 std::array<std::atomic_bool, kMaxTrackedFds> g_is_fd_owned;
30 
CrashOnFdOwnershipViolation()31 NOINLINE void CrashOnFdOwnershipViolation() {
32   RAW_LOG(ERROR, "Crashing due to FD ownership violation:\n");
33   base::debug::StackTrace().Print();
34   base::ImmediateCrash();
35 }
36 
CanTrack(int fd)37 bool CanTrack(int fd) {
38   return fd >= 0 && fd < kMaxTrackedFds;
39 }
40 
UpdateAndCheckFdOwnership(int fd,bool owned)41 void UpdateAndCheckFdOwnership(int fd, bool owned) {
42   if (CanTrack(fd) &&
43       g_is_fd_owned[static_cast<size_t>(fd)].exchange(owned) == owned &&
44       g_is_ownership_enforced) {
45     CrashOnFdOwnershipViolation();
46   }
47 }
48 
49 }  // namespace
50 
51 namespace base {
52 namespace internal {
53 
54 // static
Acquire(const ScopedFD & owner,int fd)55 void ScopedFDCloseTraits::Acquire(const ScopedFD& owner, int fd) {
56   UpdateAndCheckFdOwnership(fd, /*owned=*/true);
57 }
58 
59 // static
Release(const ScopedFD & owner,int fd)60 void ScopedFDCloseTraits::Release(const ScopedFD& owner, int fd) {
61   UpdateAndCheckFdOwnership(fd, /*owned=*/false);
62 }
63 
64 }  // namespace internal
65 
66 namespace subtle {
67 
EnableFDOwnershipEnforcement(bool enabled)68 void EnableFDOwnershipEnforcement(bool enabled) {
69   g_is_ownership_enforced = enabled;
70 }
71 
ResetFDOwnership()72 void ResetFDOwnership() {
73   std::fill(g_is_fd_owned.begin(), g_is_fd_owned.end(), false);
74 }
75 
76 }  // namespace subtle
77 
IsFDOwned(int fd)78 bool IsFDOwned(int fd) {
79   return CanTrack(fd) && g_is_fd_owned[static_cast<size_t>(fd)];
80 }
81 
82 }  // namespace base
83 
84 using LibcCloseFuncPtr = int (*)(int);
85 
86 // Load the libc close symbol to forward to from the close wrapper.
LoadCloseSymbol()87 LibcCloseFuncPtr LoadCloseSymbol() {
88 #if defined(THREAD_SANITIZER)
89   // If TSAN is enabled use __interceptor___close first to make sure the TSAN
90   // wrapper gets called.
91   return reinterpret_cast<LibcCloseFuncPtr>(
92       dlsym(RTLD_DEFAULT, "__interceptor___close"));
93 #else
94   return reinterpret_cast<LibcCloseFuncPtr>(dlsym(RTLD_NEXT, "close"));
95 #endif
96 }
97 
98 extern "C" {
99 
100 NO_SANITIZE("cfi-icall")
close(int fd)101 __attribute__((visibility("default"), noinline)) int close(int fd) {
102   static LibcCloseFuncPtr libc_close = LoadCloseSymbol();
103   if (base::IsFDOwned(fd) && g_is_ownership_enforced)
104     CrashOnFdOwnershipViolation();
105   if (libc_close == nullptr) {
106     RAW_LOG(ERROR, "close symbol missing\n");
107     base::ImmediateCrash();
108   }
109   return libc_close(fd);
110 }
111 
112 }  // extern "C"
113