Line data Source code
1 : /*
2 : Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
3 : Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
4 :
5 : This software was partially supported by the
6 : EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
7 :
8 : This software was partially supported by the
9 : ADA-FS project under the SPPEXA project funded by the DFG.
10 :
11 : This file is part of GekkoFS' POSIX interface.
12 :
13 : GekkoFS' POSIX interface is free software: you can redistribute it and/or
14 : modify it under the terms of the GNU Lesser General Public License as
15 : published by the Free Software Foundation, either version 3 of the License,
16 : or (at your option) any later version.
17 :
18 : GekkoFS' POSIX interface is distributed in the hope that it will be useful,
19 : but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : GNU Lesser General Public License for more details.
22 :
23 : You should have received a copy of the GNU Lesser General Public License
24 : along with GekkoFS' POSIX interface. If not, see
25 : <https://www.gnu.org/licenses/>.
26 :
27 : SPDX-License-Identifier: LGPL-3.0-or-later
28 : */
29 :
30 : #include <client/intercept.hpp>
31 : #include <client/preload.hpp>
32 : #include <client/hooks.hpp>
33 : #include <client/logging.hpp>
34 :
35 : #include <optional>
36 : #include <fmt/format.h>
37 :
38 : #include <cerrno>
39 :
40 : extern "C" {
41 : #include <syscall.h>
42 : #include <sys/types.h>
43 : #include <sys/socket.h>
44 : #include <printf.h>
45 : }
46 :
47 :
48 : #ifdef BYPASS_SYSCALL
49 : int (*intercept_hook_point)(long syscall_number, long arg0, long arg1,
50 : long arg2, long arg3, long arg4, long arg5,
51 : long* result){};
52 :
53 : void (*intercept_hook_point_clone_child)(unsigned long flags, void* child_stack,
54 : int* ptid, int* ctid, long newtls){};
55 :
56 : void (*intercept_hook_point_clone_parent)(unsigned long flags,
57 : void* child_stack, int* ptid,
58 : int* ctid, long newtls,
59 : long returned_pid){};
60 :
61 : void (*intercept_hook_point_post_kernel)(long syscall_number, long arg0,
62 : long arg1, long arg2, long arg3,
63 : long arg4, long arg5, long result){};
64 : #endif
65 : namespace {
66 :
67 : thread_local bool reentrance_guard_flag;
68 : thread_local gkfs::syscall::info saved_syscall_info;
69 :
70 : constexpr void
71 14111112 : save_current_syscall_info(gkfs::syscall::info info) {
72 14111112 : saved_syscall_info = info;
73 : }
74 :
75 : constexpr void
76 14107536 : reset_current_syscall_info() {
77 14107536 : saved_syscall_info = gkfs::syscall::no_info;
78 : }
79 :
80 : inline gkfs::syscall::info
81 28216582 : get_current_syscall_info() {
82 28216582 : return saved_syscall_info;
83 : }
84 :
85 :
86 : /*
87 : * hook_internal -- interception hook for internal syscalls
88 : *
89 : * This hook is basically used to keep track of file descriptors created
90 : * internally by the library itself. This is important because some
91 : * applications (e.g. ssh) may attempt to close all open file descriptors
92 : * which would leave the library internals in an inconsistent state.
93 : * We forward syscalls to the kernel but we keep track of any syscalls that may
94 : * create or destroy a file descriptor so that we can mark them as 'internal'.
95 : */
96 : inline int
97 3419195 : hook_internal(long syscall_number, long arg0, long arg1, long arg2, long arg3,
98 : long arg4, long arg5, long* result) {
99 :
100 : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
101 3419195 : const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2,
102 3419195 : arg3, arg4, arg5};
103 : #endif
104 :
105 3419195 : LOG(SYSCALL,
106 : gkfs::syscall::from_internal_code | gkfs::syscall::to_hook |
107 : gkfs::syscall::not_executed,
108 3419263 : syscall_number, args);
109 :
110 3419263 : switch(syscall_number) {
111 : #ifdef SYS_open
112 0 : case SYS_open:
113 0 : *result = syscall_no_intercept_wrapper(
114 : syscall_number, reinterpret_cast<char*>(arg0),
115 : static_cast<int>(arg1), static_cast<mode_t>(arg2));
116 :
117 0 : if(*result > 0) {
118 0 : *result = CTX->register_internal_fd(*result);
119 : }
120 :
121 : break;
122 : #endif
123 : #ifdef SYS_creat
124 0 : case SYS_creat:
125 0 : *result = syscall_no_intercept_wrapper(
126 : syscall_number, reinterpret_cast<const char*>(arg0),
127 : O_WRONLY | O_CREAT | O_TRUNC, static_cast<mode_t>(arg1));
128 0 : if(*result > 0) {
129 0 : *result = CTX->register_internal_fd(*result);
130 : }
131 :
132 : break;
133 : #endif
134 3249 : case SYS_openat:
135 3249 : *result = syscall_no_intercept_wrapper(
136 : syscall_number, static_cast<int>(arg0),
137 : reinterpret_cast<const char*>(arg1), static_cast<int>(arg2),
138 : static_cast<mode_t>(arg3));
139 3249 : if(*result > 0) {
140 2732 : *result = CTX->register_internal_fd(*result);
141 : }
142 : break;
143 : #ifdef SYS_epoll_create
144 1076 : case SYS_epoll_create:
145 1076 : *result = syscall_no_intercept_wrapper(syscall_number,
146 : static_cast<int>(arg0));
147 1076 : if(*result > 0) {
148 1076 : *result = CTX->register_internal_fd(*result);
149 : }
150 :
151 : break;
152 : #endif
153 269 : case SYS_epoll_create1:
154 269 : *result = syscall_no_intercept_wrapper(syscall_number,
155 : static_cast<int>(arg0));
156 269 : if(*result > 0) {
157 269 : *result = CTX->register_internal_fd(*result);
158 : }
159 :
160 : break;
161 :
162 0 : case SYS_dup:
163 0 : *result = syscall_no_intercept_wrapper(
164 : syscall_number, static_cast<unsigned int>(arg0));
165 0 : if(*result > 0) {
166 0 : *result = CTX->register_internal_fd(*result);
167 : }
168 : break;
169 : #ifdef SYS_dup2
170 0 : case SYS_dup2:
171 0 : *result = syscall_no_intercept_wrapper(
172 : syscall_number, static_cast<unsigned int>(arg0),
173 : static_cast<unsigned int>(arg1));
174 0 : if(*result > 0) {
175 0 : *result = CTX->register_internal_fd(*result);
176 : }
177 : break;
178 : #endif
179 0 : case SYS_dup3:
180 0 : *result = syscall_no_intercept_wrapper(
181 : syscall_number, static_cast<unsigned int>(arg0),
182 : static_cast<unsigned int>(arg1), static_cast<int>(arg2));
183 :
184 0 : if(*result > 0) {
185 0 : *result = CTX->register_internal_fd(*result);
186 : }
187 : break;
188 : #ifdef SYS_inotify_init
189 0 : case SYS_inotify_init:
190 0 : *result = syscall_no_intercept_wrapper(syscall_number);
191 :
192 0 : if(*result >= 0) {
193 0 : *result = CTX->register_internal_fd(*result);
194 : }
195 :
196 : break;
197 : #endif
198 0 : case SYS_inotify_init1:
199 0 : *result = syscall_no_intercept_wrapper(syscall_number,
200 : static_cast<int>(arg0));
201 :
202 0 : if(*result >= 0) {
203 0 : *result = CTX->register_internal_fd(*result);
204 : }
205 :
206 : break;
207 :
208 0 : case SYS_perf_event_open:
209 0 : *result = syscall_no_intercept_wrapper(
210 : syscall_number,
211 : reinterpret_cast<struct perf_event_attr*>(arg0),
212 : static_cast<pid_t>(arg1), static_cast<int>(arg2),
213 : static_cast<int>(arg3), static_cast<unsigned long>(arg4));
214 :
215 0 : if(*result >= 0) {
216 0 : *result = CTX->register_internal_fd(*result);
217 : }
218 : break;
219 : #ifdef SYS_signalfd
220 0 : case SYS_signalfd:
221 0 : *result = syscall_no_intercept_wrapper(
222 : syscall_number, static_cast<int>(arg0),
223 : reinterpret_cast<const sigset_t*>(arg1));
224 :
225 0 : if(*result >= 0) {
226 0 : *result = CTX->register_internal_fd(*result);
227 : }
228 : break;
229 : #endif
230 0 : case SYS_signalfd4:
231 0 : *result = syscall_no_intercept_wrapper(
232 : syscall_number, static_cast<int>(arg0),
233 : reinterpret_cast<const sigset_t*>(arg1),
234 : static_cast<int>(arg2));
235 :
236 0 : if(*result >= 0) {
237 0 : *result = CTX->register_internal_fd(*result);
238 : }
239 : break;
240 :
241 0 : case SYS_timerfd_create:
242 0 : *result = syscall_no_intercept_wrapper(syscall_number,
243 : static_cast<int>(arg0),
244 : static_cast<int>(arg1));
245 :
246 0 : if(*result >= 0) {
247 0 : *result = CTX->register_internal_fd(*result);
248 : }
249 : break;
250 :
251 :
252 2167 : case SYS_socket:
253 2167 : *result = syscall_no_intercept_wrapper(
254 : syscall_number, static_cast<int>(arg0),
255 : static_cast<int>(arg1), static_cast<int>(arg2));
256 :
257 2167 : if(*result >= 0) {
258 2167 : *result = CTX->register_internal_fd(*result);
259 : }
260 : break;
261 :
262 1076 : case SYS_socketpair:
263 :
264 1076 : *result = syscall_no_intercept_wrapper(
265 : syscall_number, static_cast<int>(arg0),
266 : static_cast<int>(arg1), static_cast<int>(arg2),
267 : reinterpret_cast<int*>(arg3));
268 :
269 1076 : if(*result >= 0) {
270 1076 : reinterpret_cast<int*>(arg3)[0] = CTX->register_internal_fd(
271 : reinterpret_cast<int*>(arg3)[0]);
272 2152 : reinterpret_cast<int*>(arg3)[1] = CTX->register_internal_fd(
273 1076 : reinterpret_cast<int*>(arg3)[1]);
274 : }
275 :
276 : break;
277 : #ifdef SYS_pipe
278 0 : case SYS_pipe:
279 0 : *result = syscall_no_intercept_wrapper(
280 : syscall_number, reinterpret_cast<int*>(arg0));
281 :
282 0 : if(*result >= 0) {
283 0 : reinterpret_cast<int*>(arg0)[0] = CTX->register_internal_fd(
284 : reinterpret_cast<int*>(arg0)[0]);
285 0 : reinterpret_cast<int*>(arg0)[1] = CTX->register_internal_fd(
286 0 : reinterpret_cast<int*>(arg0)[1]);
287 : }
288 :
289 : break;
290 : #endif
291 0 : case SYS_pipe2:
292 :
293 0 : *result = syscall_no_intercept_wrapper(syscall_number,
294 : reinterpret_cast<int*>(arg0),
295 : static_cast<int>(arg1));
296 0 : if(*result >= 0) {
297 0 : reinterpret_cast<int*>(arg0)[0] = CTX->register_internal_fd(
298 : reinterpret_cast<int*>(arg0)[0]);
299 0 : reinterpret_cast<int*>(arg0)[1] = CTX->register_internal_fd(
300 0 : reinterpret_cast<int*>(arg0)[1]);
301 : }
302 :
303 : break;
304 : #ifdef SYS_eventfd
305 0 : case SYS_eventfd:
306 :
307 0 : *result = syscall_no_intercept_wrapper(syscall_number,
308 : static_cast<int>(arg0));
309 :
310 0 : if(*result >= 0) {
311 0 : *result = CTX->register_internal_fd(*result);
312 : }
313 : break;
314 : #endif
315 269 : case SYS_eventfd2:
316 :
317 269 : *result = syscall_no_intercept_wrapper(syscall_number,
318 : static_cast<int>(arg0),
319 : static_cast<int>(arg1));
320 :
321 269 : if(*result >= 0) {
322 269 : *result = CTX->register_internal_fd(*result);
323 : }
324 : break;
325 :
326 3228 : case SYS_recvmsg: {
327 3228 : *result = syscall_no_intercept_wrapper(
328 : syscall_number, static_cast<int>(arg0),
329 : reinterpret_cast<struct msghdr*>(arg1),
330 : static_cast<int>(arg2));
331 :
332 : // The recvmsg() syscall can receive file descriptors from another
333 : // process that the kernel automatically adds to the client's fds
334 : // as if dup2 had been called. Whenever that happens, we need to
335 : // make sure that we register these additional fds as internal, or
336 : // we could inadvertently overwrite them
337 3228 : if(*result >= 0) {
338 3228 : auto* hdr = reinterpret_cast<struct msghdr*>(arg1);
339 3228 : struct cmsghdr* cmsg = CMSG_FIRSTHDR(hdr);
340 :
341 3228 : for(; cmsg != NULL; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
342 0 : if(cmsg->cmsg_type == SCM_RIGHTS) {
343 :
344 0 : size_t nfd = cmsg->cmsg_len > CMSG_LEN(0)
345 0 : ? (cmsg->cmsg_len - CMSG_LEN(0)) /
346 : sizeof(int)
347 : : 0;
348 :
349 0 : int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
350 :
351 0 : for(size_t i = 0; i < nfd; ++i) {
352 0 : LOG(DEBUG, "recvmsg() provided extra fd {}",
353 0 : fds[i]);
354 :
355 : // ensure we update the fds in cmsg
356 : // if they have been relocated
357 0 : fds[i] = CTX->register_internal_fd(fds[i]);
358 : }
359 : }
360 : }
361 : }
362 :
363 : break;
364 : }
365 :
366 0 : case SYS_accept:
367 0 : *result = syscall_no_intercept_wrapper(
368 : syscall_number, static_cast<int>(arg0),
369 : reinterpret_cast<struct sockaddr*>(arg1),
370 : reinterpret_cast<int*>(arg2));
371 :
372 0 : if(*result >= 0) {
373 0 : *result = CTX->register_internal_fd(*result);
374 : }
375 : break;
376 :
377 0 : case SYS_accept4:
378 0 : *result = syscall_no_intercept_wrapper(
379 : syscall_number, static_cast<int>(arg0),
380 : reinterpret_cast<struct sockaddr*>(arg1),
381 : reinterpret_cast<int*>(arg2), static_cast<int>(arg3));
382 :
383 0 : if(*result >= 0) {
384 0 : *result = CTX->register_internal_fd(*result);
385 : }
386 : break;
387 :
388 :
389 3826 : case SYS_fcntl:
390 3826 : *result = syscall_no_intercept_wrapper(
391 : syscall_number, static_cast<int>(arg0),
392 : static_cast<int>(arg1), arg2);
393 :
394 3826 : if(*result >= 0) {
395 :
396 :
397 3826 : if((static_cast<int>(arg1) == F_DUPFD ||
398 : static_cast<int>(arg1) == F_DUPFD_CLOEXEC)) {
399 0 : *result = CTX->register_internal_fd(*result);
400 : }
401 : }
402 : break;
403 :
404 4077 : case SYS_close:
405 4077 : *result = syscall_no_intercept_wrapper(syscall_number,
406 : static_cast<int>(arg0));
407 4077 : if(*result >= 0) {
408 4077 : CTX->unregister_internal_fd(arg0);
409 : }
410 : break;
411 :
412 3400026 : default:
413 : // ignore any other syscalls, i.e.: pass them on to the kernel
414 : // (syscalls forwarded to the kernel that return are logged in
415 : // hook_forwarded_syscall())
416 3400026 : ::save_current_syscall_info(gkfs::syscall::from_internal_code |
417 : gkfs::syscall::to_kernel |
418 : gkfs::syscall::not_executed);
419 3400026 : return gkfs::syscall::forward_to_kernel;
420 : }
421 :
422 19237 : LOG(SYSCALL,
423 : gkfs::syscall::from_internal_code | gkfs::syscall::to_hook |
424 : gkfs::syscall::executed,
425 : syscall_number, args, *result);
426 :
427 : return gkfs::syscall::hooked;
428 : }
429 :
430 : /*
431 : * hook -- interception hook for application syscalls
432 : *
433 : * This hook is used to implement any application filesystem-related syscalls.
434 : */
435 : inline int
436 11117815 : hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4,
437 : long arg5, long* result) {
438 :
439 : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
440 11117815 : const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2,
441 11117815 : arg3, arg4, arg5};
442 : #endif
443 :
444 11117815 : LOG(SYSCALL,
445 : gkfs::syscall::from_external_code | gkfs::syscall::to_hook |
446 : gkfs::syscall::not_executed,
447 11122573 : syscall_number, args);
448 :
449 11122573 : switch(syscall_number) {
450 :
451 0 : case SYS_execve:
452 0 : *result = syscall_no_intercept_wrapper(
453 : syscall_number, reinterpret_cast<const char*>(arg0),
454 : reinterpret_cast<const char* const*>(arg1),
455 : reinterpret_cast<const char* const*>(arg2));
456 0 : break;
457 :
458 : #ifdef SYS_execveat
459 0 : case SYS_execveat:
460 0 : *result = syscall_no_intercept_wrapper(
461 : syscall_number, arg0, reinterpret_cast<const char*>(arg1),
462 : reinterpret_cast<const char* const*>(arg2),
463 : reinterpret_cast<const char* const*>(arg3), arg4);
464 0 : break;
465 : #endif
466 : #ifdef SYS_open
467 1 : case SYS_open:
468 1 : *result = gkfs::hook::hook_openat(
469 : AT_FDCWD, reinterpret_cast<char*>(arg0),
470 : static_cast<int>(arg1), static_cast<mode_t>(arg2));
471 1 : break;
472 : #endif
473 : #ifdef SYS_creat
474 1004 : case SYS_creat:
475 1004 : *result = gkfs::hook::hook_openat(
476 : AT_FDCWD, reinterpret_cast<const char*>(arg0),
477 : O_WRONLY | O_CREAT | O_TRUNC, static_cast<mode_t>(arg1));
478 1004 : break;
479 : #endif
480 8763 : case SYS_openat:
481 8763 : *result = gkfs::hook::hook_openat(
482 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
483 : static_cast<int>(arg2), static_cast<mode_t>(arg3));
484 8763 : break;
485 :
486 12724 : case SYS_close:
487 12724 : *result = gkfs::hook::hook_close(static_cast<int>(arg0));
488 12724 : break;
489 : #ifdef SYS_stat
490 46 : case SYS_stat:
491 92 : *result =
492 46 : gkfs::hook::hook_stat(reinterpret_cast<char*>(arg0),
493 : reinterpret_cast<struct stat*>(arg1));
494 46 : break;
495 : #endif
496 : #ifdef STATX_TYPE
497 4 : case SYS_statx:
498 4 : *result = gkfs::hook::hook_statx(
499 : static_cast<int>(arg0), reinterpret_cast<char*>(arg1),
500 : static_cast<int>(arg2), static_cast<unsigned int>(arg3),
501 : reinterpret_cast<struct statx*>(arg4));
502 4 : break;
503 : #endif
504 : #ifdef SYS_lstat
505 2 : case SYS_lstat:
506 2 : *result = gkfs::hook::hook_lstat(
507 : reinterpret_cast<char*>(arg0),
508 : reinterpret_cast<struct stat*>(arg1));
509 2 : break;
510 : #endif
511 291 : case SYS_fstat:
512 291 : *result = gkfs::hook::hook_fstat(
513 : static_cast<int>(arg0),
514 : reinterpret_cast<struct stat*>(arg1));
515 291 : break;
516 :
517 0 : case SYS_newfstatat:
518 0 : *result = gkfs::hook::hook_fstatat(
519 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
520 : reinterpret_cast<struct stat*>(arg2),
521 : static_cast<int>(arg3));
522 0 : break;
523 :
524 182329 : case SYS_read:
525 182329 : *result = gkfs::hook::hook_read(static_cast<unsigned int>(arg0),
526 : reinterpret_cast<void*>(arg1),
527 : static_cast<size_t>(arg2));
528 182329 : break;
529 :
530 2 : case SYS_pread64:
531 2 : *result = gkfs::hook::hook_pread(static_cast<unsigned int>(arg0),
532 : reinterpret_cast<char*>(arg1),
533 : static_cast<size_t>(arg2),
534 : static_cast<loff_t>(arg3));
535 2 : break;
536 :
537 1 : case SYS_readv:
538 1 : *result = gkfs::hook::hook_readv(
539 : static_cast<unsigned long>(arg0),
540 : reinterpret_cast<const struct iovec*>(arg1),
541 : static_cast<unsigned long>(arg2));
542 1 : break;
543 :
544 1 : case SYS_preadv:
545 1 : *result = gkfs::hook::hook_preadv(
546 : static_cast<unsigned long>(arg0),
547 : reinterpret_cast<const struct iovec*>(arg1),
548 : static_cast<unsigned long>(arg2),
549 : static_cast<unsigned long>(arg3),
550 : static_cast<unsigned long>(arg4));
551 1 : break;
552 :
553 3 : case SYS_pwrite64:
554 3 : *result = gkfs::hook::hook_pwrite(
555 : static_cast<unsigned int>(arg0),
556 : reinterpret_cast<const char*>(arg1),
557 : static_cast<size_t>(arg2), static_cast<loff_t>(arg3));
558 3 : break;
559 180426 : case SYS_write:
560 360852 : *result =
561 180426 : gkfs::hook::hook_write(static_cast<unsigned int>(arg0),
562 : reinterpret_cast<const char*>(arg1),
563 : static_cast<size_t>(arg2));
564 180426 : break;
565 :
566 2 : case SYS_writev:
567 2 : *result = gkfs::hook::hook_writev(
568 : static_cast<unsigned long>(arg0),
569 : reinterpret_cast<const struct iovec*>(arg1),
570 : static_cast<unsigned long>(arg2));
571 2 : break;
572 :
573 2 : case SYS_pwritev:
574 2 : *result = gkfs::hook::hook_pwritev(
575 : static_cast<unsigned long>(arg0),
576 : reinterpret_cast<const struct iovec*>(arg1),
577 : static_cast<unsigned long>(arg2),
578 : static_cast<unsigned long>(arg3),
579 : static_cast<unsigned long>(arg4));
580 2 : break;
581 : #ifdef SYS_unlink
582 6 : case SYS_unlink:
583 6 : *result = gkfs::hook::hook_unlinkat(
584 : AT_FDCWD, reinterpret_cast<const char*>(arg0), 0);
585 6 : break;
586 : #endif
587 1 : case SYS_unlinkat:
588 1 : *result = gkfs::hook::hook_unlinkat(
589 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
590 : static_cast<int>(arg2));
591 1 : break;
592 : #ifdef SYS_rmdir
593 8 : case SYS_rmdir:
594 8 : *result = gkfs::hook::hook_unlinkat(
595 : AT_FDCWD, reinterpret_cast<const char*>(arg0),
596 : AT_REMOVEDIR);
597 8 : break;
598 : #endif
599 : #ifdef SYS_symlink
600 1 : case SYS_symlink:
601 1 : *result = gkfs::hook::hook_symlinkat(
602 : reinterpret_cast<const char*>(arg0), AT_FDCWD,
603 : reinterpret_cast<const char*>(arg1));
604 1 : break;
605 : #endif
606 0 : case SYS_symlinkat:
607 0 : *result = gkfs::hook::hook_symlinkat(
608 : reinterpret_cast<const char*>(arg0), static_cast<int>(arg1),
609 : reinterpret_cast<const char*>(arg2));
610 0 : break;
611 : #ifdef SYS_access
612 5 : case SYS_access:
613 10 : *result =
614 5 : gkfs::hook::hook_access(reinterpret_cast<const char*>(arg0),
615 : static_cast<int>(arg1));
616 5 : break;
617 : #endif
618 4 : case SYS_faccessat:
619 4 : *result = gkfs::hook::hook_faccessat(
620 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
621 : static_cast<int>(arg2));
622 4 : break;
623 : #ifdef SYS_faccessat2
624 : case SYS_faccessat2:
625 : *result = gkfs::hook::hook_faccessat2(
626 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
627 : static_cast<int>(arg2), static_cast<int>(arg3));
628 : break;
629 : #endif
630 8589 : case SYS_lseek:
631 8589 : *result = gkfs::hook::hook_lseek(static_cast<unsigned int>(arg0),
632 : static_cast<off_t>(arg1),
633 : static_cast<unsigned int>(arg2));
634 8589 : break;
635 :
636 7 : case SYS_truncate:
637 7 : *result = gkfs::hook::hook_truncate(
638 : reinterpret_cast<const char*>(arg0),
639 : static_cast<long>(arg1));
640 7 : break;
641 :
642 2 : case SYS_ftruncate:
643 2 : *result = gkfs::hook::hook_ftruncate(
644 : static_cast<unsigned int>(arg0),
645 : static_cast<unsigned long>(arg1));
646 2 : break;
647 :
648 2 : case SYS_dup:
649 2 : *result = gkfs::hook::hook_dup(static_cast<unsigned int>(arg0));
650 2 : break;
651 : #ifdef SYS_dup2
652 2 : case SYS_dup2:
653 2 : *result = gkfs::hook::hook_dup2(static_cast<unsigned int>(arg0),
654 : static_cast<unsigned int>(arg1));
655 2 : break;
656 : #endif
657 2 : case SYS_dup3:
658 2 : *result = gkfs::hook::hook_dup3(static_cast<unsigned int>(arg0),
659 : static_cast<unsigned int>(arg1),
660 : static_cast<int>(arg2));
661 2 : break;
662 : #ifdef SYS_getdents
663 0 : case SYS_getdents:
664 0 : *result = gkfs::hook::hook_getdents(
665 : static_cast<unsigned int>(arg0),
666 : reinterpret_cast<struct linux_dirent*>(arg1),
667 : static_cast<unsigned int>(arg2));
668 0 : break;
669 : #endif
670 36 : case SYS_getdents64:
671 36 : *result = gkfs::hook::hook_getdents64(
672 : static_cast<unsigned int>(arg0),
673 : reinterpret_cast<struct linux_dirent64*>(arg1),
674 : static_cast<unsigned int>(arg2));
675 36 : break;
676 :
677 1 : case SYS_mkdirat:
678 1 : *result = gkfs::hook::hook_mkdirat(
679 : static_cast<unsigned int>(arg0),
680 : reinterpret_cast<const char*>(arg1),
681 : static_cast<mode_t>(arg2));
682 1 : break;
683 : #ifdef SYS_mkdir
684 18 : case SYS_mkdir:
685 18 : *result = gkfs::hook::hook_mkdirat(
686 : AT_FDCWD, reinterpret_cast<const char*>(arg0),
687 : static_cast<mode_t>(arg1));
688 18 : break;
689 : #endif
690 : #ifdef SYS_chmod
691 1 : case SYS_chmod:
692 1 : *result = gkfs::hook::hook_fchmodat(AT_FDCWD,
693 : reinterpret_cast<char*>(arg0),
694 : static_cast<mode_t>(arg1));
695 1 : break;
696 : #endif
697 2 : case SYS_fchmod:
698 2 : *result = gkfs::hook::hook_fchmod(static_cast<unsigned int>(arg0),
699 : static_cast<mode_t>(arg1));
700 2 : break;
701 :
702 2 : case SYS_fchmodat:
703 2 : *result = gkfs::hook::hook_fchmodat(static_cast<unsigned int>(arg0),
704 : reinterpret_cast<char*>(arg1),
705 : static_cast<mode_t>(arg2));
706 2 : break;
707 :
708 0 : case SYS_flock:
709 0 : *result = gkfs::hook::hook_flock(static_cast<unsigned int>(arg0),
710 : static_cast<unsigned int>(arg1));
711 0 : break;
712 9 : case SYS_chdir:
713 18 : *result =
714 9 : gkfs::hook::hook_chdir(reinterpret_cast<const char*>(arg0));
715 9 : break;
716 :
717 3 : case SYS_fchdir:
718 3 : *result = gkfs::hook::hook_fchdir(static_cast<unsigned int>(arg0));
719 3 : break;
720 :
721 5 : case SYS_getcwd:
722 5 : *result = gkfs::hook::hook_getcwd(reinterpret_cast<char*>(arg0),
723 : static_cast<unsigned long>(arg1));
724 5 : break;
725 : #ifdef SYS_readlink
726 0 : case SYS_readlink:
727 0 : *result = gkfs::hook::hook_readlinkat(
728 : AT_FDCWD, reinterpret_cast<const char*>(arg0),
729 : reinterpret_cast<char*>(arg1), static_cast<int>(arg2));
730 0 : break;
731 : #endif
732 1 : case SYS_readlinkat:
733 1 : *result = gkfs::hook::hook_readlinkat(
734 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
735 : reinterpret_cast<char*>(arg2), static_cast<int>(arg3));
736 1 : break;
737 :
738 17160 : case SYS_fcntl:
739 17160 : *result = gkfs::hook::hook_fcntl(static_cast<unsigned int>(arg0),
740 : static_cast<unsigned int>(arg1),
741 : static_cast<unsigned long>(arg2));
742 17160 : break;
743 : #ifdef SYS_rename
744 12 : case SYS_rename:
745 12 : *result = gkfs::hook::hook_renameat(
746 : AT_FDCWD, reinterpret_cast<const char*>(arg0), AT_FDCWD,
747 : reinterpret_cast<const char*>(arg1), 0);
748 12 : break;
749 : #endif
750 2 : case SYS_renameat:
751 2 : *result = gkfs::hook::hook_renameat(
752 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
753 : static_cast<int>(arg2), reinterpret_cast<const char*>(arg3),
754 : 0);
755 2 : break;
756 :
757 0 : case SYS_renameat2:
758 0 : *result = gkfs::hook::hook_renameat(
759 : static_cast<int>(arg0), reinterpret_cast<const char*>(arg1),
760 : static_cast<int>(arg2), reinterpret_cast<const char*>(arg3),
761 : static_cast<unsigned int>(arg4));
762 0 : break;
763 :
764 2 : case SYS_fstatfs:
765 2 : *result = gkfs::hook::hook_fstatfs(
766 : static_cast<unsigned int>(arg0),
767 : reinterpret_cast<struct statfs*>(arg1));
768 2 : break;
769 :
770 1 : case SYS_statfs:
771 1 : *result = gkfs::hook::hook_statfs(
772 : reinterpret_cast<const char*>(arg0),
773 : reinterpret_cast<struct statfs*>(arg1));
774 1 : break;
775 :
776 1 : case SYS_fdatasync:
777 1 : case SYS_fsync:
778 1 : *result = gkfs::hook::hook_fsync(static_cast<unsigned int>(arg0));
779 1 : break;
780 :
781 1 : case SYS_getxattr:
782 1 : *result = gkfs::hook::hook_getxattr(
783 : reinterpret_cast<const char*>(arg0),
784 : reinterpret_cast<const char*>(arg1),
785 : reinterpret_cast<void*>(arg2), static_cast<size_t>(arg4));
786 1 : break;
787 :
788 10711086 : default:
789 : // ignore any other syscalls, i.e.: pass them on to the kernel
790 : // (syscalls forwarded to the kernel that return are logged in
791 : // hook_forwarded_syscall())
792 10711086 : ::save_current_syscall_info(gkfs::syscall::from_external_code |
793 : gkfs::syscall::to_kernel |
794 : gkfs::syscall::not_executed);
795 10711086 : return gkfs::syscall::forward_to_kernel;
796 : }
797 :
798 411487 : LOG(SYSCALL,
799 : gkfs::syscall::from_external_code | gkfs::syscall::to_hook |
800 : gkfs::syscall::executed,
801 : syscall_number, args, *result);
802 :
803 : return gkfs::syscall::hooked;
804 : }
805 :
806 : #ifdef SYS_socketcall
807 : /* Wraps socketcall in powerpc9, we only change syscalls that need special
808 : * treatment */
809 : long
810 : socketcall_wrapper(long syscall_number, long& arg0, long& arg1, long& arg2,
811 : long& arg3, long& arg4, long& arg5) {
812 :
813 : switch(static_cast<int>(arg0)) {
814 : case 1:
815 : syscall_number = SYS_socket;
816 : break;
817 : case 5:
818 : syscall_number = SYS_accept;
819 : break;
820 :
821 : case 17:
822 : syscall_number = SYS_recvmsg;
823 : break;
824 : case 18:
825 : syscall_number = SYS_accept4;
826 : break;
827 : case 19:
828 : syscall_number = SYS_recvmmsg;
829 : break;
830 :
831 : default:
832 : break;
833 : }
834 : if(syscall_number != SYS_socketcall) {
835 : long int* parameters = (long int*) arg1;
836 : arg0 = static_cast<long>(*parameters);
837 : parameters++;
838 : arg1 = static_cast<long>(*parameters);
839 : parameters++;
840 : arg2 = static_cast<long>(*parameters);
841 : parameters++;
842 : arg3 = static_cast<long>(*parameters);
843 : parameters++;
844 : arg4 = static_cast<long>(*parameters);
845 : parameters++;
846 : arg5 = static_cast<long>(*parameters);
847 : }
848 :
849 : return syscall_number;
850 : }
851 : #endif
852 :
853 :
854 : void
855 14108155 : hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2,
856 : long arg3, long arg4, long arg5, long result) {
857 :
858 14108155 : if(::get_current_syscall_info() == gkfs::syscall::no_info) {
859 0 : return;
860 : }
861 :
862 : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
863 14108155 : const long args[gkfs::syscall::MAX_ARGS] = {arg0, arg1, arg2,
864 14108155 : arg3, arg4, arg5};
865 : #endif
866 :
867 14108155 : LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed,
868 14107536 : syscall_number, args, result);
869 :
870 14107536 : ::reset_current_syscall_info();
871 : }
872 :
873 : void
874 1097 : hook_clone_at_child(unsigned long flags, void* child_stack, int* ptid,
875 : int* ctid, long newtls) {
876 :
877 : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
878 1097 : const long args[gkfs::syscall::MAX_ARGS] = {
879 1097 : static_cast<long>(flags), reinterpret_cast<long>(child_stack),
880 : reinterpret_cast<long>(ptid), reinterpret_cast<long>(ctid),
881 1097 : static_cast<long>(newtls), 0};
882 : #endif
883 :
884 1097 : reentrance_guard_flag = true;
885 :
886 1097 : LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed,
887 1097 : SYS_clone, args, 0);
888 :
889 1097 : reentrance_guard_flag = false;
890 1097 : }
891 :
892 : void
893 1097 : hook_clone_at_parent(unsigned long flags, void* child_stack, int* ptid,
894 : int* ctid, long newtls, long returned_pid) {
895 :
896 : #if defined(GKFS_ENABLE_LOGGING) && defined(GKFS_DEBUG_BUILD)
897 1097 : const long args[gkfs::syscall::MAX_ARGS] = {
898 1097 : static_cast<long>(flags), reinterpret_cast<long>(child_stack),
899 : reinterpret_cast<long>(ptid), reinterpret_cast<long>(ctid),
900 1097 : static_cast<long>(newtls), 0};
901 : #endif
902 :
903 1097 : reentrance_guard_flag = true;
904 :
905 1097 : LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed,
906 1097 : SYS_clone, args, returned_pid);
907 :
908 1097 : reentrance_guard_flag = false;
909 1097 : }
910 :
911 : } // namespace
912 :
913 : namespace gkfs::preload {
914 :
915 : int
916 3282814 : internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1,
917 : long arg2, long arg3, long arg4, long arg5,
918 : long* syscall_return_value) {
919 3282814 : assert(CTX->interception_enabled());
920 :
921 : #ifdef SYS_socketcall
922 : if(syscall_number == SYS_socketcall)
923 : syscall_number = socketcall_wrapper(syscall_number, arg0, arg1, arg2,
924 : arg3, arg4, arg5);
925 : #endif
926 :
927 3282705 : if(reentrance_guard_flag) {
928 0 : ::save_current_syscall_info(gkfs::syscall::from_internal_code |
929 : gkfs::syscall::to_kernel |
930 : gkfs::syscall::not_executed);
931 0 : return gkfs::syscall::forward_to_kernel;
932 : }
933 :
934 3282705 : int was_hooked = 0;
935 :
936 3282705 : reentrance_guard_flag = true;
937 3282705 : was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4,
938 : arg5, syscall_return_value);
939 3282717 : reentrance_guard_flag = false;
940 :
941 3282717 : return was_hooked;
942 : }
943 :
944 :
945 : /*
946 : * hook_guard_wrapper -- a wrapper which can notice reentrance.
947 : *
948 : * The reentrance_guard_flag flag allows the library to distinguish the hooking
949 : * of its own syscalls. E.g. while handling an open() syscall,
950 : * libgkfs_intercept might call fopen(), which in turn uses an open()
951 : * syscall internally. This internally used open() syscall is once again
952 : * forwarded to libgkfs_intercept, but using this flag we can notice this
953 : * case of reentering itself.
954 : *
955 : * XXX This approach still contains a very significant bug, as libgkfs_intercept
956 : * being called inside a signal handler might easily forward a mock fd to the
957 : * kernel.
958 : */
959 : int
960 11255415 : hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2,
961 : long arg3, long arg4, long arg5,
962 : long* syscall_return_value) {
963 :
964 11255415 : assert(CTX->interception_enabled());
965 :
966 : #ifdef SYS_socketcall
967 : if(syscall_number == SYS_socketcall)
968 : syscall_number = socketcall_wrapper(syscall_number, arg0, arg1, arg2,
969 : arg3, arg4, arg5);
970 : #endif
971 :
972 11254910 : int was_hooked = 0;
973 :
974 11254910 : if(reentrance_guard_flag) {
975 :
976 136515 : was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4,
977 : arg5, syscall_return_value);
978 136515 : return was_hooked;
979 : }
980 :
981 11118395 : reentrance_guard_flag = true;
982 :
983 11118395 : was_hooked = ::hook(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5,
984 : syscall_return_value);
985 :
986 11119207 : reentrance_guard_flag = false;
987 :
988 11119207 : return was_hooked;
989 : }
990 :
991 : void
992 269 : start_self_interception() {
993 :
994 269 : LOG(DEBUG, "Enabling syscall interception for self");
995 :
996 269 : intercept_hook_point = internal_hook_guard_wrapper;
997 269 : intercept_hook_point_post_kernel = hook_forwarded_syscall;
998 269 : intercept_hook_point_clone_child = hook_clone_at_child;
999 269 : intercept_hook_point_clone_parent = hook_clone_at_parent;
1000 269 : }
1001 :
1002 : void
1003 269 : start_interception() {
1004 :
1005 269 : assert(CTX->interception_enabled());
1006 :
1007 269 : LOG(DEBUG, "Enabling syscall interception for client process");
1008 :
1009 : // Set up the callback function pointer
1010 269 : intercept_hook_point = hook_guard_wrapper;
1011 269 : intercept_hook_point_post_kernel = hook_forwarded_syscall;
1012 269 : intercept_hook_point_clone_child = hook_clone_at_child;
1013 269 : intercept_hook_point_clone_parent = hook_clone_at_parent;
1014 269 : }
1015 :
1016 : void
1017 269 : stop_interception() {
1018 269 : assert(CTX->interception_enabled());
1019 :
1020 269 : LOG(DEBUG, "Disabling syscall interception for client process");
1021 :
1022 : // Reset callback function pointer
1023 269 : intercept_hook_point = nullptr;
1024 269 : intercept_hook_point_post_kernel = nullptr;
1025 269 : intercept_hook_point_clone_child = nullptr;
1026 269 : intercept_hook_point_clone_parent = nullptr;
1027 269 : }
1028 :
1029 : } // namespace gkfs::preload
|