/** * Replace current @tracee's syscall with an older and compatible one * whenever it's required, i.e. when the syscall is supported by the * kernel as specified by @config->virtual_release but it isn't * supported by the actual kernel. */ static int handle_sysenter_end(Tracee *tracee, Config *config) { /* Note: syscalls like "openat" can be replaced by "open" since PRoot * has canonicalized "fd + path" into "path". */ switch (get_sysnum(tracee, ORIGINAL)) { case PR_accept4: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,28), .new_sysarg_num = PR_accept, .shifts = NONE }; modify_syscall(tracee, config, &modif); return 0; } case PR_dup3: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_dup2, .shifts = NONE }; /* "If oldfd equals newfd, then dup3() fails with the * error EINVAL" -- man dup3 */ if (peek_reg(tracee, CURRENT, SYSARG_1) == peek_reg(tracee, CURRENT, SYSARG_2)) return -EINVAL; modify_syscall(tracee, config, &modif); return 0; } case PR_epoll_create1: { bool modified; Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_epoll_create, .shifts = NONE }; /* "the size argument is ignored, but must be greater * than zero" -- man epoll_create */ modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_1, 1); return 0; } case PR_epoll_pwait: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,19), .new_sysarg_num = PR_epoll_wait, .shifts = NONE }; modify_syscall(tracee, config, &modif); return 0; } case PR_eventfd2: { bool modified; word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_eventfd, .shifts = NONE }; modified = modify_syscall(tracee, config, &modif); if (modified) { /* EFD_SEMAPHORE can't be emulated with eventfd. */ flags = peek_reg(tracee, CURRENT, SYSARG_2); if ((flags & EFD_SEMAPHORE) != 0) return -EINVAL; } return 0; } case PR_faccessat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_access, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_fchmodat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_chmod, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_fchownat: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_5); modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? PR_lchown : PR_chown); modify_syscall(tracee, config, &modif); return 0; } case PR_fcntl: { word_t command; if (!needs_kompat(config, KERNEL_VERSION(2,6,24))) return 0; command = peek_reg(tracee, ORIGINAL, SYSARG_2); if (command == F_DUPFD_CLOEXEC) poke_reg(tracee, SYSARG_2, F_DUPFD); return 0; } case PR_newfstatat: case PR_fstatat64: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_4); if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0) return -EINVAL; /* Exposed by LTP. */ #if defined(ARCH_X86_64) if ((flags & AT_SYMLINK_NOFOLLOW) != 0) modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64); else modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64); #else if ((flags & AT_SYMLINK_NOFOLLOW) != 0) modif.new_sysarg_num = PR_lstat64; else modif.new_sysarg_num = PR_stat64; #endif modify_syscall(tracee, config, &modif); return 0; } case PR_futex: { word_t operation; static bool warned = false; if (!needs_kompat(config, KERNEL_VERSION(2,6,22)) || config->actual_release == 0) return 0; operation = peek_reg(tracee, CURRENT, SYSARG_2); if ((operation & FUTEX_PRIVATE_FLAG) == 0) return 0; if (!warned) { warned = true; note(tracee, WARNING, USER, "kompat: this kernel doesn't support private futexes " "and PRoot can't emulate them. Expect some troubles..."); } poke_reg(tracee, SYSARG_2, operation & ~FUTEX_PRIVATE_FLAG); return 0; } case PR_futimesat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_utimes, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; } case PR_inotify_init1: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_inotify_init, .shifts = NONE }; modify_syscall(tracee, config, &modif); return 0; } case PR_linkat: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_link, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 1, .offset = -1 }, [1] = { .sysarg = SYSARG_4, .nb_args = 1, .offset = -2 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_5); if ((flags & ~AT_SYMLINK_FOLLOW) != 0) return -EINVAL; /* Exposed by LTP. */ modify_syscall(tracee, config, &modif); return 0; } case PR_mkdirat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_mkdir, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return 0; }
/** * Replace current @tracee's syscall with an older and compatible one * whenever it's required, i.e. when the syscall is supported by the * kernel as specified by @config->release but it isn't supported by * the actual kernel. */ static void handle_sysenter_end(Tracee *tracee, Config *config) { bool modified; /* Note: syscalls like "openat" can be replaced by "open" since PRoot * has canonicalized "fd + path" into "path". */ switch (get_sysnum(tracee, ORIGINAL)) { case PR_accept4: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,28), .new_sysarg_num = PR_accept, .shifts = {{0}} }; modify_syscall(tracee, config, &modif); return; } case PR_dup3: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_dup2, .shifts = {{0}} }; modify_syscall(tracee, config, &modif); return; } case PR_epoll_create1: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_epoll_create, .shifts = {{0}} }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_1, 0); /* Force "size" to 0. */ return; } case PR_epoll_pwait: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,19), .new_sysarg_num = PR_epoll_wait, .shifts = {{0}} }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_5, 0); /* Force "sigmask" to 0. */ return; } case PR_eventfd2: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_eventfd, .shifts = {{0}} }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_2, 0); /* Force "flags" to 0. */ return; } case PR_faccessat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_access, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ return; } case PR_fchmodat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_chmod, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ return; } case PR_fchownat: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 3, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_5); modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? PR_lchown : PR_chown); modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0. */ return; } case PR_newfstatat: case PR_fstatat64: { word_t flags; Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; flags = peek_reg(tracee, CURRENT, SYSARG_4); #if defined(ARCH_X86_64) if ((flags & AT_SYMLINK_NOFOLLOW) != 0) modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64); else modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64); #else if ((flags & AT_SYMLINK_NOFOLLOW) != 0) modif.new_sysarg_num = PR_lstat64; else modif.new_sysarg_num = PR_stat64; #endif modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0. */ return; } case PR_futimesat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_utimes, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return; } case PR_inotify_init1: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,27), .new_sysarg_num = PR_inotify_init, .shifts = {{0}} }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_1, 0); /* Force "flags" to 0. */ return; } case PR_linkat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_link, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 1, .offset = -1 }, [1] = { .sysarg = SYSARG_4, .nb_args = 1, .offset = -2 } } }; modified = modify_syscall(tracee, config, &modif); if (modified) poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0. */ return; } case PR_mkdirat: { Modif modif = { .expected_release = KERNEL_VERSION(2,6,16), .new_sysarg_num = PR_mkdir, .shifts = { [0] = { .sysarg = SYSARG_2, .nb_args = 2, .offset = -1 } } }; modify_syscall(tracee, config, &modif); return; }