diff options
| -rw-r--r-- | AGENTS.md | 14 | ||||
| -rw-r--r-- | ioriot/src/generate/gioop.c | 28 | ||||
| -rw-r--r-- | ioriot/src/replay/rioop.c | 36 | ||||
| -rw-r--r-- | ioriot/src/replay/rioop.h | 1 | ||||
| -rw-r--r-- | scripts/syscall_compat32.c | 134 | ||||
| -rw-r--r-- | systemtap/src/ioriot.stp | 20 | ||||
| -rw-r--r-- | systemtap/src/javaioriot.stp | 20 | ||||
| -rw-r--r-- | systemtap/src/targetedioriot.stp | 20 |
8 files changed, 248 insertions, 25 deletions
@@ -246,3 +246,17 @@ attempts to exercise these operations: If the generated replay only contains the header and `#INIT`, or the replay paths escape the intended test tree, investigate capture tokenization and dirfd-relative path handling first. + +## Legacy Compat Syscalls + +The normal helper above exercises the current x86_64 syscall surface. For +legacy compat names that only show up through the 32-bit ABI on this host, +use [scripts/syscall_compat32.c](/home/paul/git/ioriot/scripts/syscall_compat32.c) +and compile it with `cc -m32`. + +- It reuses the same `PID_FILE GO_FILE BASE_DIR USER` contract as the main + matrix helper, so the capture/generate/init/replay procedure above applies + unchanged. +- It covers `readdir`, `llseek`, `statfs64`, `fstatfs64`, `chown16`, + `lchown16`, and `fchown16`. +- Run it with a user whose uid/gid fit into the legacy 16-bit chown ABI. diff --git a/ioriot/src/generate/gioop.c b/ioriot/src/generate/gioop.c index f089a2c..887496b 100644 --- a/ioriot/src/generate/gioop.c +++ b/ioriot/src/generate/gioop.c @@ -59,6 +59,15 @@ _gioop_creat_flags(const gtask_s *t) return O_CREAT | O_WRONLY | O_TRUNC; } +static const char* +_gioop_label(const gtask_s *t, const char *fallback) +{ + if (t != NULL && t->op != NULL) + return t->op; + + return fallback; +} + static const gioop_entry_s _GIOOP_PREOPEN[] = { {"open", gioop_open}, {"openat", gioop_openat}, @@ -112,6 +121,7 @@ static const gioop_entry_s _GIOOP_DISPATCH[] = { {"lchown", gioop_lchown}, {"lchown16", gioop_lchown}, {"fchown", gioop_fchown}, + {"fchown16", gioop_fchown}, {"fchownat", gioop_chown}, {"exit_group", gioop_exit_group}, }; @@ -138,6 +148,9 @@ void gioop_test(void) assert(_gioop_creat_flags(&task) == (O_CREAT | O_WRONLY | O_TRUNC)); task.flags = O_RDONLY; assert(_gioop_creat_flags(&task) == O_RDONLY); + task.op = "lchown16"; + assert(Eq(_gioop_label(&task, "fallback"), "lchown16")); + assert(Eq(_gioop_label(NULL, "fallback"), "fallback")); } status_e gioop_run(gwriter_s *w, gtask_s *t) @@ -771,13 +784,16 @@ status_e gioop_fchmod(gwriter_s *w, gtask_s *t, generate_s *g) status_e gioop_chown(gwriter_s *w, gtask_s *t, generate_s *g) { + const char *label = _gioop_label(t, "chown"); + if (t->path == NULL) { return ERROR; } generate_vsize_by_path(g, t, NULL); // Hmm, maybe rename t->offset, because here it is used for the user UID - Gioop_write(CHOWN, "%s|%ld|%d|%d|chown", t->path, t->offset, t->G, t->status); + Gioop_write(CHOWN, "%s|%ld|%d|%d|%s", + t->path, t->offset, t->G, t->status, label); if (t->status == 0) vsize_stat(t->vsize, t->path); @@ -787,26 +803,32 @@ status_e gioop_chown(gwriter_s *w, gtask_s *t, generate_s *g) status_e gioop_fchown(gwriter_s *w, gtask_s *t, generate_s *g) { + const char *label = _gioop_label(t, "fchown"); + if (!t->has_fd) { return ERROR; } generate_vsize_by_path(g, t, t->vfd->path); // Hmm, maybe rename t->offset, because here it is used for the user UID - Gioop_write(FCHOWN, "%ld|%ld|%d|%d|fchown", t->mapped_fd, t->offset, t->G, t->status); + Gioop_write(FCHOWN, "%ld|%ld|%d|%d|%s", + t->mapped_fd, t->offset, t->G, t->status, label); return SUCCESS; } status_e gioop_lchown(gwriter_s *w, gtask_s *t, generate_s *g) { + const char *label = _gioop_label(t, "lchown"); + if (t->path == NULL) { return ERROR; } generate_vsize_by_path(g, t, NULL); // Hmm, maybe rename t->offset, because here it is used for the user UID - Gioop_write(LCHOWN, "%s|%ld|%d|%d|chown", t->path, t->offset, t->G, t->status); + Gioop_write(LCHOWN, "%s|%ld|%d|%d|%s", + t->path, t->offset, t->G, t->status, label); if (t->status == 0) vsize_stat(t->vsize, t->path); diff --git a/ioriot/src/replay/rioop.c b/ioriot/src/replay/rioop.c index 2b3d98c..0d20eff 100644 --- a/ioriot/src/replay/rioop.c +++ b/ioriot/src/replay/rioop.c @@ -100,6 +100,19 @@ _rioop_open_creat(rprocess_s *p, rthread_s *t, rtask_s *task) rioop_open(p, t, task, O_CREAT|O_WRONLY|O_TRUNC); } +static DIR* +_rioop_get_dir_stream(vfd_s *vfd) +{ + if (vfd == NULL) + return NULL; + + if (vfd->dirfd != NULL) + return vfd->dirfd; + + vfd->dirfd = fdopendir(vfd->fd); + return vfd->dirfd; +} + static rioop_handler_f _rioop_find_handler(const rioop_entry_s *entries, const size_t num_entries, const int op) @@ -147,14 +160,18 @@ static const rioop_entry_s _RIOOP_HANDLERS[] = { {SYNC_FILE_RANGE, _rioop_noop}, {FCNTL, rioop_fcntl}, {GETDENTS, rioop_getdents}, + {READDIR, rioop_readdir}, {LSEEK, rioop_lseek}, {LLSEEK, rioop_lseek}, {CHMOD, rioop_chmod}, {FCHMOD, rioop_fchmod}, {CHOWN, rioop_chown}, + {CHOWN16, rioop_chown}, {FCHOWN, rioop_fchown}, + {FCHOWN16, rioop_fchown}, {FCHOWNAT, rioop_fchown}, {LCHOWN, rioop_lchown}, + {LCOWN16, rioop_lchown}, {META_EXIT_GROUP, _rioop_noop}, {META_TIMELINE, _rioop_noop}, }; @@ -169,6 +186,8 @@ void rioop_test(void) _rioop_open_creat); assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, READAHEAD) == _rioop_noop); + assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, READDIR) == + rioop_readdir); assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, -1) == NULL); } @@ -320,10 +339,9 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task) _Init_fd(3); _Init_virtfd; - // getdents expects a dirfd - DIR *dirfd = fdopendir(vfd->fd); - if (dirfd) { - vfd->dirfd = dirfd; + // Directory readers reuse the same DIR* stream so offsets stay aligned. + DIR *dirfd = _rioop_get_dir_stream(vfd); + if (dirfd != NULL) { readdir(dirfd); #ifdef THREAD_DEBUG fprintf(t->rthread_fd, "TRACE OPEN|fdopendir|%s|\n", vfd->path); @@ -332,6 +350,16 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task) } } +void rioop_readdir(rprocess_s *p, rthread_s *t, rtask_s *task) +{ + _Init_fd(3); + _Init_virtfd; + + DIR *dirfd = _rioop_get_dir_stream(vfd); + if (dirfd != NULL) + readdir(dirfd); +} + void rioop_mkdir(rprocess_s *p, rthread_s *t, rtask_s *task) { _Init_path(3); diff --git a/ioriot/src/replay/rioop.h b/ioriot/src/replay/rioop.h index aff2a21..5d0db2b 100644 --- a/ioriot/src/replay/rioop.h +++ b/ioriot/src/replay/rioop.h @@ -40,6 +40,7 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task); void rioop_mkdir(rprocess_s *p, rthread_s *t, rtask_s *task); void rioop_open(rprocess_s *p, rthread_s *t, rtask_s *task, int flags_); void rioop_read(rprocess_s *p, rthread_s *t, rtask_s *task); +void rioop_readdir(rprocess_s *p, rthread_s *t, rtask_s *task); void rioop_rename(rprocess_s *p, rthread_s *t, rtask_s *task); void rioop_stat(rprocess_s *p, rthread_s *t, rtask_s *task); void rioop_lseek(rprocess_s *p, rthread_s *t, rtask_s *task); diff --git a/scripts/syscall_compat32.c b/scripts/syscall_compat32.c new file mode 100644 index 0000000..03a3474 --- /dev/null +++ b/scripts/syscall_compat32.c @@ -0,0 +1,134 @@ +#define _GNU_SOURCE +#define _LARGEFILE64_SOURCE + +#include <asm/unistd_32.h> +#include <fcntl.h> +#include <limits.h> +#include <pwd.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/types.h> +#include <unistd.h> + +#if !defined(__i386__) +#error "Compile this helper with -m32 so it exercises the compat syscall ABI." +#endif + +static void +die(const char *msg) +{ + perror(msg); + exit(1); +} + +static void +must(bool ok, const char *msg) +{ + if (!ok) + die(msg); +} + +static void +must_eq(long rc, const char *msg) +{ + if (rc == -1) + die(msg); +} + +static void +write_pid_file(const char *path) +{ + FILE *fp = fopen(path, "w"); + + must(fp != NULL, "fopen pid file"); + fprintf(fp, "%ld\n", (long)getpid()); + fclose(fp); +} + +static void +wait_for_go(const char *path) +{ + while (access(path, F_OK) != 0) + usleep(10000); +} + +static void +write_full(int fd, const char *buf, size_t len) +{ + while (len > 0) { + ssize_t written = write(fd, buf, len); + + must_eq(written, "write"); + buf += written; + len -= (size_t)written; + } +} + +int +main(int argc, char **argv) +{ + if (argc != 5) { + fprintf(stderr, "usage: %s PID_FILE GO_FILE BASE_DIR USER\n", argv[0]); + return 2; + } + + const char *pid_file = argv[1]; + const char *go_file = argv[2]; + const char *base = argv[3]; + const char *user = argv[4]; + struct passwd *pw = getpwnam(user); + + must(pw != NULL, "getpwnam"); + must(pw->pw_uid <= USHRT_MAX, "uid fits 16-bit compat ABI"); + must(pw->pw_gid <= USHRT_MAX, "gid fits 16-bit compat ABI"); + + write_pid_file(pid_file); + wait_for_go(go_file); + + char path_file[1024]; + char path_link[1024]; + + snprintf(path_file, sizeof(path_file), "%s/compat-open.txt", base); + snprintf(path_link, sizeof(path_link), "%s/compat-link.txt", base); + + must_eq(mkdir(base, 0777), "mkdir compat base"); + + int dirfd = open(base, O_RDONLY | O_DIRECTORY); + + must_eq(dirfd, "open compat base"); + + int fd = open(path_file, O_CREAT | O_RDWR | O_TRUNC, 0644); + + must_eq(fd, "open compat-open.txt"); + write_full(fd, "compat-data", 11); + must_eq(lseek(fd, 0, SEEK_SET), "lseek compat-open.txt"); + must_eq(symlink(path_file, path_link), "symlink compat-link.txt"); + + char dir_buf[4096]; + long long llseek_result = 0; + struct statfs64 sfs; + + must_eq(syscall(__NR_readdir, dirfd, dir_buf, sizeof(dir_buf)), + "compat readdir"); + must_eq(syscall(__NR__llseek, fd, 0UL, 0UL, &llseek_result, SEEK_END), + "compat _llseek"); + must_eq(syscall(__NR_statfs64, base, sizeof(sfs), &sfs), + "compat statfs64"); + must_eq(syscall(__NR_fstatfs64, fd, sizeof(sfs), &sfs), + "compat fstatfs64"); + must_eq(syscall(__NR_chown, path_file, pw->pw_uid, pw->pw_gid), + "compat chown16"); + must_eq(syscall(__NR_lchown, path_link, pw->pw_uid, pw->pw_gid), + "compat lchown16"); + must_eq(syscall(__NR_fchown, fd, pw->pw_uid, pw->pw_gid), + "compat fchown16"); + + must_eq(close(fd), "close compat-open.txt"); + must_eq(close(dirfd), "close compat base"); + + return 0; +} diff --git a/systemtap/src/ioriot.stp b/systemtap/src/ioriot.stp index 9bdf440..9103a25 100644 --- a/systemtap/src/ioriot.stp +++ b/systemtap/src/ioriot.stp @@ -944,9 +944,12 @@ probe syscall.fchmod.return { # --- chown/lchown --- # Tapset entry vars: path_unquoted, owner, group -# Note: chown16/lchown16 do not exist on x86_64 +# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat +# workloads may emit chown16/lchown16 on the same kernel. probe syscall.chown, - syscall.lchown { + syscall.chown16, + syscall.lchown, + syscall.lchown16 { if (execname() != "stapio") { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_PATH[tid(),name] = path_unquoted @@ -956,7 +959,9 @@ probe syscall.chown, } probe syscall.chown.return, - syscall.lchown.return { + syscall.chown16.return, + syscall.lchown.return, + syscall.lchown16.return { if(execname() != "stapio") { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n", @@ -975,8 +980,10 @@ probe syscall.chown.return, # --- fchown --- # Tapset entry vars: fd, owner, group -# Note: fchown16 does not exist on x86_64 -probe syscall.fchown { +# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may +# emit fchown16 on the same kernel. +probe syscall.fchown, + syscall.fchown16 { if (execname() != "stapio") { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_FD[tid(),name] = fd @@ -985,7 +992,8 @@ probe syscall.fchown { } } -probe syscall.fchown.return { +probe syscall.fchown.return, + syscall.fchown16.return { if(execname() != "stapio") { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n", diff --git a/systemtap/src/javaioriot.stp b/systemtap/src/javaioriot.stp index cf7e797..0c59bf2 100644 --- a/systemtap/src/javaioriot.stp +++ b/systemtap/src/javaioriot.stp @@ -944,9 +944,12 @@ probe syscall.fchmod.return { # --- chown/lchown --- # Tapset entry vars: path_unquoted, owner, group -# Note: chown16/lchown16 do not exist on x86_64 +# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat +# workloads may emit chown16/lchown16 on the same kernel. probe syscall.chown, - syscall.lchown { + syscall.chown16, + syscall.lchown, + syscall.lchown16 { if (execname() == "java") { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_PATH[tid(),name] = path_unquoted @@ -956,7 +959,9 @@ probe syscall.chown, } probe syscall.chown.return, - syscall.lchown.return { + syscall.chown16.return, + syscall.lchown.return, + syscall.lchown16.return { if(execname() == "java") { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n", @@ -975,8 +980,10 @@ probe syscall.chown.return, # --- fchown --- # Tapset entry vars: fd, owner, group -# Note: fchown16 does not exist on x86_64 -probe syscall.fchown { +# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may +# emit fchown16 on the same kernel. +probe syscall.fchown, + syscall.fchown16 { if (execname() == "java") { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_FD[tid(),name] = fd @@ -985,7 +992,8 @@ probe syscall.fchown { } } -probe syscall.fchown.return { +probe syscall.fchown.return, + syscall.fchown16.return { if(execname() == "java") { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n", diff --git a/systemtap/src/targetedioriot.stp b/systemtap/src/targetedioriot.stp index dccfe4c..7253b65 100644 --- a/systemtap/src/targetedioriot.stp +++ b/systemtap/src/targetedioriot.stp @@ -944,9 +944,12 @@ probe syscall.fchmod.return { # --- chown/lchown --- # Tapset entry vars: path_unquoted, owner, group -# Note: chown16/lchown16 do not exist on x86_64 +# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat +# workloads may emit chown16/lchown16 on the same kernel. probe syscall.chown, - syscall.lchown { + syscall.chown16, + syscall.lchown, + syscall.lchown16 { if (pid() == target()) { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_PATH[tid(),name] = path_unquoted @@ -956,7 +959,9 @@ probe syscall.chown, } probe syscall.chown.return, - syscall.lchown.return { + syscall.chown16.return, + syscall.lchown.return, + syscall.lchown16.return { if(pid() == target()) { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n", @@ -975,8 +980,10 @@ probe syscall.chown.return, # --- fchown --- # Tapset entry vars: fd, owner, group -# Note: fchown16 does not exist on x86_64 -probe syscall.fchown { +# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may +# emit fchown16 on the same kernel. +probe syscall.fchown, + syscall.fchown16 { if (pid() == target()) { PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns() ENTRY_FD[tid(),name] = fd @@ -985,7 +992,8 @@ probe syscall.fchown { } } -probe syscall.fchown.return { +probe syscall.fchown.return, + syscall.fchown16.return { if(pid() == target()) { ns = gettimeofday_ns() printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n", |
