OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/test/posix/close_multiple.h" | |
16 | |
17 #include <dirent.h> | |
18 #include <errno.h> | |
19 #include <fcntl.h> | |
20 #include <limits.h> | |
21 #include <stdlib.h> | |
22 #include <string.h> | |
23 #include <unistd.h> | |
24 | |
25 #include <algorithm> | |
26 | |
27 #include "base/basictypes.h" | |
28 #include "base/logging.h" | |
29 #include "base/memory/scoped_ptr.h" | |
30 #include "base/posix/eintr_wrapper.h" | |
31 #include "build/build_config.h" | |
32 #include "util/numeric/safe_assignment.h" | |
33 | |
34 // Everything in this file is expected to execute between fork() and exec(), | |
35 // so everything called here must be acceptable in this context. However, | |
36 // logging code that is not expected to execute under normal circumstances is | |
37 // currently permitted. | |
38 | |
39 namespace crashpad { | |
40 namespace { | |
41 | |
42 // This function attempts to close |fd| or mark it as close-on-exec. On systems | |
43 // where close-on-exec is attempted, a failure to mark it close-on-exec will be | |
44 // followed by an attempt to close it. |ebadf_ok| should be set to |true| if | |
45 // the caller is attempting to close the file descriptor “blind,” that is, | |
46 // without knowledge that it is or is not a valid file descriptor. | |
47 void CloseNowOrOnExec(int fd, bool ebadf_ok) { | |
48 int rv; | |
49 | |
50 #if defined(OS_MACOSX) | |
51 // Try to set close-on-exec, to avoid attempting to close a guarded FD with | |
52 // a close guard set. | |
53 rv = fcntl(fd, F_SETFD, FD_CLOEXEC); | |
54 if (rv != -1 || (ebadf_ok && errno == EBADF)) { | |
55 return; | |
56 } | |
57 PLOG(WARNING) << "fcntl"; | |
58 #endif | |
59 | |
60 rv = IGNORE_EINTR(close(fd)); | |
61 if (rv != 0 && !(ebadf_ok && errno == EBADF)) { | |
62 PLOG(WARNING) << "close"; | |
63 } | |
64 } | |
65 | |
66 struct ScopedDIRCloser { | |
67 void operator()(DIR* dir) const { | |
68 if (dir) { | |
69 if (closedir(dir) < 0) { | |
70 PLOG(ERROR) << "closedir"; | |
71 } | |
72 } | |
73 } | |
74 }; | |
75 | |
76 using ScopedDIR = scoped_ptr<DIR, ScopedDIRCloser>; | |
77 | |
78 // This function implements CloseMultipleNowOrOnExec() using an operating | |
79 // system-specific FD directory to determine which file descriptors are open. | |
80 // This is an advantage over looping over all possible file descriptors, because | |
81 // no attempt needs to be made to close file descriptors that are not open. | |
82 bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { | |
83 #if defined(OS_MACOSX) | |
84 const char kFDDir[] = "/dev/fd"; | |
85 #elif defined(OS_LINUX) | |
86 const char kFDDir[] = "/proc/self/fd"; | |
87 #endif | |
88 | |
89 DIR* dir = opendir(kFDDir); | |
90 if (!dir) { | |
91 PLOG(WARNING) << "opendir"; | |
92 return false; | |
93 } | |
94 | |
95 ScopedDIR dir_owner(dir); | |
96 | |
97 int dir_fd = dirfd(dir); | |
98 if (dir_fd == -1) { | |
99 PLOG(WARNING) << "dirfd"; | |
100 return false; | |
101 } | |
102 | |
103 dirent entry; | |
104 dirent* result; | |
105 int rv; | |
106 while ((rv = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) { | |
107 const char* entry_name = &(*result->d_name); | |
108 if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) { | |
109 continue; | |
110 } | |
111 | |
112 char* end; | |
113 long entry_fd_long = strtol(entry_name, &end, 10); | |
114 if (entry_name[0] == '\0' || *end) { | |
115 LOG(ERROR) << "unexpected entry " << entry_name; | |
116 return false; | |
117 } | |
118 | |
119 int entry_fd; | |
120 if (!AssignIfInRange(&entry_fd, entry_fd_long)) { | |
121 LOG(ERROR) << "out-of-range fd " << entry_name; | |
122 return false; | |
123 } | |
124 | |
125 if (entry_fd >= fd && entry_fd != preserve_fd && entry_fd != dir_fd) { | |
126 CloseNowOrOnExec(entry_fd, false); | |
127 } | |
128 } | |
129 | |
130 return true; | |
131 } | |
132 | |
133 } // namespace | |
134 | |
135 void CloseMultipleNowOrOnExec(int fd, int preserve_fd) { | |
136 if (CloseMultipleNowOrOnExecUsingFDDir(fd, preserve_fd)) { | |
137 return; | |
138 } | |
139 | |
140 // Fallback: close every file descriptor starting at |fd| and ending at the | |
141 // system’s file descriptor limit. Check a few values and use the highest as | |
142 // the limit, because these may be based on the file descriptor limit set by | |
143 // setrlimit(), and higher-numbered file descriptors may have been opened | |
144 // prior to the limit being lowered. For Mac OS X, see 10.9.2 | |
145 // Libc-997.90.3/gen/FreeBSD/sysconf.c sysconf() and 10.9.4 | |
146 // xnu-2422.110.17/bsd/kern/kern_descrip.c getdtablesize(), which both return | |
147 // the current RLIMIT_NOFILE value, not the maximum possible file descriptor. | |
148 int max_fd = std::max(implicit_cast<int>(sysconf(_SC_OPEN_MAX)), OPEN_MAX); | |
149 max_fd = std::max(max_fd, getdtablesize()); | |
150 | |
151 for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) { | |
152 if (entry_fd != preserve_fd) { | |
153 CloseNowOrOnExec(entry_fd, true); | |
154 } | |
155 } | |
156 } | |
157 | |
158 } // namespace crashpad | |
OLD | NEW |