282895911c65569c6a5050bb859b9e73383c1a5a
[lttng-ust.git] / liblttng-ust-comm / lttng-ust-fd-tracker.c
1 /*
2 * Copyright (C) 2016 - Aravind HT <aravind.ht@gmail.com>
3 * 2016 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; only
8 * version 2.1 of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #define _GNU_SOURCE
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <sys/select.h>
31 #include <sys/resource.h>
32 #include <sys/time.h>
33 #include <fcntl.h>
34 #include <pthread.h>
35 #include <urcu/compiler.h>
36 #include <urcu/tls-compat.h>
37 #include <urcu/system.h>
38
39 #include <ust-fd.h>
40 #include <helper.h>
41 #include <lttng/ust-error.h>
42 #include <usterr-signal-safe.h>
43
44 #include "../liblttng-ust/compat.h"
45
46 /* Operations on the fd set. */
47 #define IS_FD_VALID(fd) ((fd) >= 0 && (fd) < lttng_ust_max_fd)
48 #define GET_FD_SET_FOR_FD(fd, fd_sets) (&((fd_sets)[(fd) / FD_SETSIZE]))
49 #define CALC_INDEX_TO_SET(fd) ((fd) % FD_SETSIZE)
50 #define IS_FD_STD(fd) (IS_FD_VALID(fd) && (fd) <= STDERR_FILENO)
51
52 /* Check fd validity before calling these. */
53 #define ADD_FD_TO_SET(fd, fd_sets) \
54 FD_SET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets))
55 #define IS_FD_SET(fd, fd_sets) \
56 FD_ISSET(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets))
57 #define DEL_FD_FROM_SET(fd, fd_sets) \
58 FD_CLR(CALC_INDEX_TO_SET(fd), GET_FD_SET_FOR_FD(fd, fd_sets))
59
60 /*
61 * Protect the lttng_fd_set. Nests within the ust_lock, and therefore
62 * within the libc dl lock. Therefore, we need to fixup the TLS before
63 * nesting into this lock.
64 *
65 * The ust_safe_guard_fd_mutex nests within the ust_mutex. This mutex
66 * is also held across fork.
67 */
68 static pthread_mutex_t ust_safe_guard_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
69 /*
70 * Track whether we are within lttng-ust or application, for close
71 * system call override by LD_PRELOAD library.
72 */
73 static DEFINE_URCU_TLS(int, thread_fd_tracking);
74
75 /* fd_set used to book keep fd being used by lttng-ust. */
76 static fd_set *lttng_fd_set;
77 static int lttng_ust_max_fd;
78 static int num_fd_sets;
79 static int init_done;
80
81 /*
82 * Force a read (imply TLS fixup for dlopen) of TLS variables.
83 */
84 void lttng_ust_fixup_fd_tracker_tls(void)
85 {
86 asm volatile ("" : : "m" (URCU_TLS(thread_fd_tracking)));
87 }
88
89 /*
90 * Allocate the fd set array based on the hard limit set for this
91 * process. This will be called during the constructor execution
92 * and will also be called in the child after fork via lttng_ust_init.
93 */
94 void lttng_ust_init_fd_tracker(void)
95 {
96 struct rlimit rlim;
97 int i;
98
99 if (CMM_LOAD_SHARED(init_done))
100 return;
101
102 memset(&rlim, 0, sizeof(rlim));
103 /* Get the current possible max number of fd for this process. */
104 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
105 abort();
106 /*
107 * FD set array size determined using the hard limit. Even if
108 * the process wishes to increase its limit using setrlimit, it
109 * can only do so with the softlimit which will be less than the
110 * hard limit.
111 */
112 lttng_ust_max_fd = rlim.rlim_max;
113 num_fd_sets = lttng_ust_max_fd / FD_SETSIZE;
114 if (lttng_ust_max_fd % FD_SETSIZE)
115 ++num_fd_sets;
116 if (lttng_fd_set != NULL) {
117 free(lttng_fd_set);
118 lttng_fd_set = NULL;
119 }
120 lttng_fd_set = malloc(num_fd_sets * (sizeof(fd_set)));
121 if (!lttng_fd_set)
122 abort();
123 for (i = 0; i < num_fd_sets; i++)
124 FD_ZERO((&lttng_fd_set[i]));
125 CMM_STORE_SHARED(init_done, 1);
126 }
127
128 void lttng_ust_lock_fd_tracker(void)
129 {
130 URCU_TLS(thread_fd_tracking) = 1;
131 /*
132 * Ensure the compiler don't move the store after the close()
133 * call in case close() would be marked as leaf.
134 */
135 cmm_barrier();
136 pthread_mutex_lock(&ust_safe_guard_fd_mutex);
137 }
138
139 void lttng_ust_unlock_fd_tracker(void)
140 {
141 pthread_mutex_unlock(&ust_safe_guard_fd_mutex);
142 /*
143 * Ensure the compiler don't move the store before the close()
144 * call, in case close() would be marked as leaf.
145 */
146 cmm_barrier();
147 URCU_TLS(thread_fd_tracking) = 0;
148 }
149
150 static int dup_std_fd(int fd)
151 {
152 int ret, i;
153 int fd_to_close[STDERR_FILENO + 1];
154 int fd_to_close_count = 0;
155 int dup_cmd = F_DUPFD; /* Default command */
156 int fd_valid = -1;
157
158 if (!(IS_FD_STD(fd))) {
159 /* Should not be here */
160 ret = -1;
161 goto error;
162 }
163
164 /* Check for FD_CLOEXEC flag */
165 ret = fcntl(fd, F_GETFD);
166 if (ret < 0) {
167 PERROR("fcntl on f_getfd");
168 ret = -1;
169 goto error;
170 }
171
172 if (ret & FD_CLOEXEC) {
173 dup_cmd = F_DUPFD_CLOEXEC;
174 }
175
176 /* Perform dup */
177 for (i = 0; i < STDERR_FILENO + 1; i++) {
178 ret = fcntl(fd, dup_cmd, 0);
179 if (ret < 0) {
180 PERROR("fcntl dup fd");
181 goto error;
182 }
183
184 if (!(IS_FD_STD(ret))) {
185 /* fd is outside of STD range, use it. */
186 fd_valid = ret;
187 /* Close fd received as argument. */
188 fd_to_close[i] = fd;
189 fd_to_close_count++;
190 break;
191 }
192
193 fd_to_close[i] = ret;
194 fd_to_close_count++;
195 }
196
197 /* Close intermediary fds */
198 for (i = 0; i < fd_to_close_count; i++) {
199 ret = close(fd_to_close[i]);
200 if (ret) {
201 PERROR("close on temporary fd: %d.", fd_to_close[i]);
202 /*
203 * Not using an abort here would yield a complicated
204 * error handling for the caller. If a failure occurs
205 * here, the system is already in a bad state.
206 */
207 abort();
208 }
209 }
210
211 ret = fd_valid;
212 error:
213 return ret;
214 }
215
216 /*
217 * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd.
218 * Has strict checking of fd validity.
219 *
220 * If fd <= 2, dup the fd until fd > 2. This enables us to bypass
221 * problems that can be encountered if UST uses stdin, stdout, stderr
222 * fds for internal use (daemon etc.). This can happen if the
223 * application closes either of those file descriptors. Intermediary fds
224 * are closed as needed.
225 *
226 * Return -1 on error.
227 *
228 */
229 int lttng_ust_add_fd_to_tracker(int fd)
230 {
231 int ret;
232 /*
233 * Ensure the tracker is initialized when called from
234 * constructors.
235 */
236 lttng_ust_init_fd_tracker();
237 assert(URCU_TLS(thread_fd_tracking));
238
239 if (IS_FD_STD(fd)) {
240 ret = dup_std_fd(fd);
241 if (ret < 0) {
242 goto error;
243 }
244 fd = ret;
245 }
246
247 /* Trying to add an fd which we can not accommodate. */
248 assert(IS_FD_VALID(fd));
249 /* Setting an fd thats already set. */
250 assert(!IS_FD_SET(fd, lttng_fd_set));
251
252 ADD_FD_TO_SET(fd, lttng_fd_set);
253 return fd;
254 error:
255 return ret;
256 }
257
258 /*
259 * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd.
260 * Has strict checking for fd validity.
261 */
262 void lttng_ust_delete_fd_from_tracker(int fd)
263 {
264 /*
265 * Ensure the tracker is initialized when called from
266 * constructors.
267 */
268 lttng_ust_init_fd_tracker();
269
270 assert(URCU_TLS(thread_fd_tracking));
271 /* Not a valid fd. */
272 assert(IS_FD_VALID(fd));
273 /* Deleting an fd which was not set. */
274 assert(IS_FD_SET(fd, lttng_fd_set));
275
276 DEL_FD_FROM_SET(fd, lttng_fd_set);
277 }
278
279 /*
280 * Interface allowing applications to close arbitrary file descriptors.
281 * We check if it is owned by lttng-ust, and return -1, errno=EBADF
282 * instead of closing it if it is the case.
283 */
284 int lttng_ust_safe_close_fd(int fd, int (*close_cb)(int fd))
285 {
286 int ret = 0;
287
288 lttng_ust_fixup_fd_tracker_tls();
289
290 /*
291 * Ensure the tracker is initialized when called from
292 * constructors.
293 */
294 lttng_ust_init_fd_tracker();
295
296 /*
297 * If called from lttng-ust, we directly call close without
298 * validating whether the FD is part of the tracked set.
299 */
300 if (URCU_TLS(thread_fd_tracking))
301 return close_cb(fd);
302
303 lttng_ust_lock_fd_tracker();
304 if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) {
305 ret = -1;
306 errno = EBADF;
307 } else {
308 ret = close_cb(fd);
309 }
310 lttng_ust_unlock_fd_tracker();
311
312 return ret;
313 }
314
315 /*
316 * Interface allowing applications to close arbitrary streams.
317 * We check if it is owned by lttng-ust, and return -1, errno=EBADF
318 * instead of closing it if it is the case.
319 */
320 int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream))
321 {
322 int ret = 0, fd;
323
324 lttng_ust_fixup_fd_tracker_tls();
325
326 /*
327 * Ensure the tracker is initialized when called from
328 * constructors.
329 */
330 lttng_ust_init_fd_tracker();
331
332 /*
333 * If called from lttng-ust, we directly call fclose without
334 * validating whether the FD is part of the tracked set.
335 */
336 if (URCU_TLS(thread_fd_tracking))
337 return fclose_cb(stream);
338
339 fd = fileno(stream);
340
341 lttng_ust_lock_fd_tracker();
342 if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) {
343 ret = -1;
344 errno = EBADF;
345 } else {
346 ret = fclose_cb(stream);
347 }
348 lttng_ust_unlock_fd_tracker();
349
350 return ret;
351 }
352
353 #ifdef __OpenBSD__
354 static void set_close_success(int *p)
355 {
356 *p = 1;
357 }
358 static int test_close_success(const int *p)
359 {
360 return *p;
361 }
362 #else
363 static void set_close_success(int *p __attribute__((unused)))
364 {
365 }
366 static int test_close_success(const int *p __attribute__((unused)))
367 {
368 return 1;
369 }
370 #endif
371
372 /*
373 * Implement helper for closefrom() override.
374 */
375 int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int fd))
376 {
377 int ret = 0, close_success = 0, i;
378
379 lttng_ust_fixup_fd_tracker_tls();
380
381 /*
382 * Ensure the tracker is initialized when called from
383 * constructors.
384 */
385 lttng_ust_init_fd_tracker();
386
387 if (lowfd < 0) {
388 /*
389 * NetBSD return EBADF if fd is invalid.
390 */
391 errno = EBADF;
392 ret = -1;
393 goto end;
394 }
395 /*
396 * If called from lttng-ust, we directly call close without
397 * validating whether the FD is part of the tracked set.
398 */
399 if (URCU_TLS(thread_fd_tracking)) {
400 for (i = lowfd; i < lttng_ust_max_fd; i++) {
401 if (close_cb(i) < 0) {
402 switch (errno) {
403 case EBADF:
404 continue;
405 case EINTR:
406 default:
407 ret = -1;
408 goto end;
409 }
410 }
411 set_close_success(&close_success);
412 }
413 } else {
414 lttng_ust_lock_fd_tracker();
415 for (i = lowfd; i < lttng_ust_max_fd; i++) {
416 if (IS_FD_VALID(i) && IS_FD_SET(i, lttng_fd_set))
417 continue;
418 if (close_cb(i) < 0) {
419 switch (errno) {
420 case EBADF:
421 continue;
422 case EINTR:
423 default:
424 ret = -1;
425 lttng_ust_unlock_fd_tracker();
426 goto end;
427 }
428 }
429 set_close_success(&close_success);
430 }
431 lttng_ust_unlock_fd_tracker();
432 }
433 if (!test_close_success(&close_success)) {
434 /*
435 * OpenBSD return EBADF if fd is greater than all open
436 * file descriptors.
437 */
438 ret = -1;
439 errno = EBADF;
440 }
441 end:
442 return ret;
443 }
This page took 0.037224 seconds and 3 git commands to generate.