1bc186bf08453f870001446ebf17dbb0e2ab2054
[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 static pthread_mutex_t ust_safe_guard_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
66 /*
67 * Track whether we are within lttng-ust or application, for close
68 * system call override by LD_PRELOAD library.
69 */
70 static DEFINE_URCU_TLS(int, thread_fd_tracking);
71
72 /* fd_set used to book keep fd being used by lttng-ust. */
73 static fd_set *lttng_fd_set;
74 static int lttng_ust_max_fd;
75 static int num_fd_sets;
76 static int init_done;
77
78 /*
79 * Force a read (imply TLS fixup for dlopen) of TLS variables.
80 */
81 void lttng_ust_fixup_fd_tracker_tls(void)
82 {
83 asm volatile ("" : : "m" (URCU_TLS(thread_fd_tracking)));
84 }
85
86 /*
87 * Allocate the fd set array based on the hard limit set for this
88 * process. This will be called during the constructor execution
89 * and will also be called in the child after fork via lttng_ust_init.
90 */
91 void lttng_ust_init_fd_tracker(void)
92 {
93 struct rlimit rlim;
94 int i;
95
96 if (CMM_LOAD_SHARED(init_done))
97 return;
98
99 memset(&rlim, 0, sizeof(rlim));
100 /* Get the current possible max number of fd for this process. */
101 if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
102 abort();
103 /*
104 * FD set array size determined using the hard limit. Even if
105 * the process wishes to increase its limit using setrlimit, it
106 * can only do so with the softlimit which will be less than the
107 * hard limit.
108 */
109 lttng_ust_max_fd = rlim.rlim_max;
110 num_fd_sets = lttng_ust_max_fd / FD_SETSIZE;
111 if (lttng_ust_max_fd % FD_SETSIZE)
112 ++num_fd_sets;
113 if (lttng_fd_set != NULL) {
114 free(lttng_fd_set);
115 lttng_fd_set = NULL;
116 }
117 lttng_fd_set = malloc(num_fd_sets * (sizeof(fd_set)));
118 if (!lttng_fd_set)
119 abort();
120 for (i = 0; i < num_fd_sets; i++)
121 FD_ZERO((&lttng_fd_set[i]));
122 CMM_STORE_SHARED(init_done, 1);
123 }
124
125 void lttng_ust_lock_fd_tracker(void)
126 {
127 URCU_TLS(thread_fd_tracking) = 1;
128 /*
129 * Ensure the compiler don't move the store after the close()
130 * call in case close() would be marked as leaf.
131 */
132 cmm_barrier();
133 pthread_mutex_lock(&ust_safe_guard_fd_mutex);
134 }
135
136 void lttng_ust_unlock_fd_tracker(void)
137 {
138 pthread_mutex_unlock(&ust_safe_guard_fd_mutex);
139 /*
140 * Ensure the compiler don't move the store before the close()
141 * call, in case close() would be marked as leaf.
142 */
143 cmm_barrier();
144 URCU_TLS(thread_fd_tracking) = 0;
145 }
146
147 static int dup_std_fd(int fd)
148 {
149 int ret, i;
150 int fd_to_close[STDERR_FILENO + 1];
151 int fd_to_close_count = 0;
152 int dup_cmd = F_DUPFD; /* Default command */
153 int fd_valid = -1;
154
155 if (!(IS_FD_STD(fd))) {
156 /* Should not be here */
157 ret = -1;
158 goto error;
159 }
160
161 /* Check for FD_CLOEXEC flag */
162 ret = fcntl(fd, F_GETFD);
163 if (ret < 0) {
164 PERROR("fcntl on f_getfd");
165 ret = -1;
166 goto error;
167 }
168
169 if (ret & FD_CLOEXEC) {
170 dup_cmd = F_DUPFD_CLOEXEC;
171 }
172
173 /* Perform dup */
174 for (i = 0; i < STDERR_FILENO + 1; i++) {
175 ret = fcntl(fd, dup_cmd, 0);
176 if (ret < 0) {
177 PERROR("fcntl dup fd");
178 goto error;
179 }
180
181 if (!(IS_FD_STD(ret))) {
182 /* fd is outside of STD range, use it. */
183 fd_valid = ret;
184 /* Close fd received as argument. */
185 fd_to_close[i] = fd;
186 fd_to_close_count++;
187 break;
188 }
189
190 fd_to_close[i] = ret;
191 fd_to_close_count++;
192 }
193
194 /* Close intermediary fds */
195 for (i = 0; i < fd_to_close_count; i++) {
196 ret = close(fd_to_close[i]);
197 if (ret) {
198 PERROR("close on temporary fd: %d.", fd_to_close[i]);
199 /*
200 * Not using an abort here would yield a complicated
201 * error handling for the caller. If a failure occurs
202 * here, the system is already in a bad state.
203 */
204 abort();
205 }
206 }
207
208 ret = fd_valid;
209 error:
210 return ret;
211 }
212
213 /*
214 * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd.
215 * Has strict checking of fd validity.
216 *
217 * If fd <= 2, dup the fd until fd > 2. This enables us to bypass
218 * problems that can be encountered if UST uses stdin, stdout, stderr
219 * fds for internal use (daemon etc.). This can happen if the
220 * application closes either of those file descriptors. Intermediary fds
221 * are closed as needed.
222 *
223 * Return -1 on error.
224 *
225 */
226 int lttng_ust_add_fd_to_tracker(int fd)
227 {
228 int ret;
229 /*
230 * Ensure the tracker is initialized when called from
231 * constructors.
232 */
233 lttng_ust_init_fd_tracker();
234 assert(URCU_TLS(thread_fd_tracking));
235
236 if (IS_FD_STD(fd)) {
237 ret = dup_std_fd(fd);
238 if (ret < 0) {
239 goto error;
240 }
241 fd = ret;
242 }
243
244 /* Trying to add an fd which we can not accommodate. */
245 assert(IS_FD_VALID(fd));
246 /* Setting an fd thats already set. */
247 assert(!IS_FD_SET(fd, lttng_fd_set));
248
249 ADD_FD_TO_SET(fd, lttng_fd_set);
250 return fd;
251 error:
252 return ret;
253 }
254
255 /*
256 * Needs to be called with ust_safe_guard_fd_mutex held when opening the fd.
257 * Has strict checking for fd validity.
258 */
259 void lttng_ust_delete_fd_from_tracker(int fd)
260 {
261 /*
262 * Ensure the tracker is initialized when called from
263 * constructors.
264 */
265 lttng_ust_init_fd_tracker();
266
267 assert(URCU_TLS(thread_fd_tracking));
268 /* Not a valid fd. */
269 assert(IS_FD_VALID(fd));
270 /* Deleting an fd which was not set. */
271 assert(IS_FD_SET(fd, lttng_fd_set));
272
273 DEL_FD_FROM_SET(fd, lttng_fd_set);
274 }
275
276 /*
277 * Interface allowing applications to close arbitrary file descriptors.
278 * We check if it is owned by lttng-ust, and return -1, errno=EBADF
279 * instead of closing it if it is the case.
280 */
281 int lttng_ust_safe_close_fd(int fd, int (*close_cb)(int fd))
282 {
283 int ret = 0;
284
285 lttng_ust_fixup_fd_tracker_tls();
286
287 /*
288 * Ensure the tracker is initialized when called from
289 * constructors.
290 */
291 lttng_ust_init_fd_tracker();
292
293 /*
294 * If called from lttng-ust, we directly call close without
295 * validating whether the FD is part of the tracked set.
296 */
297 if (URCU_TLS(thread_fd_tracking))
298 return close_cb(fd);
299
300 lttng_ust_lock_fd_tracker();
301 if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) {
302 ret = -1;
303 errno = EBADF;
304 } else {
305 ret = close_cb(fd);
306 }
307 lttng_ust_unlock_fd_tracker();
308
309 return ret;
310 }
311
312 /*
313 * Interface allowing applications to close arbitrary streams.
314 * We check if it is owned by lttng-ust, and return -1, errno=EBADF
315 * instead of closing it if it is the case.
316 */
317 int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream))
318 {
319 int ret = 0, fd;
320
321 lttng_ust_fixup_fd_tracker_tls();
322
323 /*
324 * Ensure the tracker is initialized when called from
325 * constructors.
326 */
327 lttng_ust_init_fd_tracker();
328
329 /*
330 * If called from lttng-ust, we directly call fclose without
331 * validating whether the FD is part of the tracked set.
332 */
333 if (URCU_TLS(thread_fd_tracking))
334 return fclose_cb(stream);
335
336 fd = fileno(stream);
337
338 lttng_ust_lock_fd_tracker();
339 if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) {
340 ret = -1;
341 errno = EBADF;
342 } else {
343 ret = fclose_cb(stream);
344 }
345 lttng_ust_unlock_fd_tracker();
346
347 return ret;
348 }
349
350 #ifdef __OpenBSD__
351 static void set_close_success(int *p)
352 {
353 *p = 1;
354 }
355 static int test_close_success(const int *p)
356 {
357 return *p;
358 }
359 #else
360 static void set_close_success(int *p __attribute__((unused)))
361 {
362 }
363 static int test_close_success(const int *p __attribute__((unused)))
364 {
365 return 1;
366 }
367 #endif
368
369 /*
370 * Implement helper for closefrom() override.
371 */
372 int lttng_ust_safe_closefrom_fd(int lowfd, int (*close_cb)(int fd))
373 {
374 int ret = 0, close_success = 0, i;
375
376 lttng_ust_fixup_fd_tracker_tls();
377
378 /*
379 * Ensure the tracker is initialized when called from
380 * constructors.
381 */
382 lttng_ust_init_fd_tracker();
383
384 if (lowfd < 0) {
385 /*
386 * NetBSD return EBADF if fd is invalid.
387 */
388 errno = EBADF;
389 ret = -1;
390 goto end;
391 }
392 /*
393 * If called from lttng-ust, we directly call close without
394 * validating whether the FD is part of the tracked set.
395 */
396 if (URCU_TLS(thread_fd_tracking)) {
397 for (i = lowfd; i < lttng_ust_max_fd; i++) {
398 if (close_cb(i) < 0) {
399 switch (errno) {
400 case EBADF:
401 continue;
402 case EINTR:
403 default:
404 ret = -1;
405 goto end;
406 }
407 }
408 set_close_success(&close_success);
409 }
410 } else {
411 lttng_ust_lock_fd_tracker();
412 for (i = lowfd; i < lttng_ust_max_fd; i++) {
413 if (IS_FD_VALID(i) && IS_FD_SET(i, lttng_fd_set))
414 continue;
415 if (close_cb(i) < 0) {
416 switch (errno) {
417 case EBADF:
418 continue;
419 case EINTR:
420 default:
421 ret = -1;
422 lttng_ust_unlock_fd_tracker();
423 goto end;
424 }
425 }
426 set_close_success(&close_success);
427 }
428 lttng_ust_unlock_fd_tracker();
429 }
430 if (!test_close_success(&close_success)) {
431 /*
432 * OpenBSD return EBADF if fd is greater than all open
433 * file descriptors.
434 */
435 ret = -1;
436 errno = EBADF;
437 }
438 end:
439 return ret;
440 }
This page took 0.037191 seconds and 3 git commands to generate.