The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=6c744621c72062edf176ecf28b92ad10e8264454
commit 6c744621c72062edf176ecf28b92ad10e8264454 Author: Ishan Agrawal <[email protected]> AuthorDate: 2026-06-24 05:20:15 +0000 Commit: Kristof Provost <[email protected]> CommitDate: 2026-06-24 14:50:04 +0000 truss: add AF_NETLINK syscall argument decoding support Track AF_NETLINK socket file descriptors in procinfo using fd_domain entries and use them to detect Netlink traffic across later syscalls. Add Netlink payload decoding support for sendto(), recvfrom(), and sendmsg() paths by integrating sysdecode_netlink() into BinString and Msghdr argument handling, with fallback to existing binary/iovec printing when decoding fails. Reviewed by: kp Signed-off-by: Ishan Agrawal <[email protected]> Sponsored by: Google LLC (GSoC 2026) Pull Request: https://github.com/freebsd/freebsd-src/pull/2295 --- usr.bin/truss/setup.c | 40 ++++++++++++++++++++++++++-- usr.bin/truss/syscall.h | 2 +- usr.bin/truss/syscalls.c | 68 ++++++++++++++++++++++++++---------------------- usr.bin/truss/truss.h | 9 +++++++ 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c index d5fa1ed50ce1..ecc4a1792144 100644 --- a/usr.bin/truss/setup.c +++ b/usr.bin/truss/setup.c @@ -340,6 +340,7 @@ new_proc(struct trussinfo *info, pid_t pid, lwpid_t lwpid) np->pid = pid; np->abi = find_abi(pid); LIST_INIT(&np->threadlist); + LIST_INIT(&np->fdlist); LIST_INSERT_HEAD(&info->proclist, np, entries); if (lwpid != 0) @@ -352,10 +353,16 @@ static void free_proc(struct procinfo *p) { struct threadinfo *t, *t2; + struct fd_domain *f, *f2; LIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) { free(t); } + + LIST_FOREACH_SAFE(f, &p->fdlist, entries, f2) { + free(f); + } + LIST_REMOVE(p, entries); free(p); } @@ -492,7 +499,7 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t, #endif if (!(sc->decode.args[i].type & OUT)) { t->cs.s_args[i] = print_arg(&sc->decode.args[i], - t->cs.args, NULL, info); + t->cs.args, NULL, info, &sc->decode); } } #if DEBUG @@ -562,12 +569,41 @@ exit_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) (long)t->cs.args[sc->decode.args[i].offset]); } else { temp = print_arg(&sc->decode.args[i], - t->cs.args, psr.sr_retval, info); + t->cs.args, psr.sr_retval, info, + &sc->decode); } t->cs.s_args[i] = temp; } } + /* + * Track successfully created sockets so later syscalls using the + * returned file descriptor can be identified by socket domain. + */ + if (strcmp(sc->name, "socket") == 0 && + psr.sr_error == 0) { + + struct fd_domain *f = calloc(1, sizeof(*f)); + + f->fd = (int)psr.sr_retval[0]; + f->domain = (int)t->cs.args[0]; + + LIST_INSERT_HEAD(&p->fdlist, f, entries); + } + + if (strcmp(sc->name, "close") == 0 && + psr.sr_error == 0) { + + struct fd_domain *f, *f2; + + LIST_FOREACH_SAFE(f, &p->fdlist, entries, f2) { + if (f->fd == (int)t->cs.args[0]) { + LIST_REMOVE(f, entries); + free(f); + } + } + } + print_syscall_ret(info, psr.sr_error, psr.sr_retval); free_syscall(t); diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index 47d973326dfb..2f98636173b7 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -229,7 +229,7 @@ struct syscall { struct syscall *get_syscall(struct threadinfo *, u_int, u_int); char *print_arg(struct syscall_arg *, syscallarg_t *, syscallarg_t *, - struct trussinfo *); + struct trussinfo *, struct syscall_decode *); /* * Linux Socket defines diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index abb9d18d6783..dbc621124071 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -1569,39 +1569,40 @@ user_ptr32_to_psaddr(int32_t user_pointer) return ((psaddr_t)(uintptr_t)user_pointer); } +/* + * Check whether a file descriptor belongs to an AF_NETLINK socket + * for the current traced process. + */ +static bool +is_netlink(struct trussinfo *trussinfo, int num_fd) +{ + struct procinfo *p = trussinfo->curthread->proc; + struct fd_domain *f; + + LIST_FOREACH(f, &p->fdlist, entries) { + if (f->fd == num_fd && f->domain == AF_NETLINK) + return (true); + } + return (false); +} + #define NETLINK_MAX_DECODE 4096 /* - * Reads the first IOV and attempts to print it as Netlink using libsysdecode. - * Returns true if successful, false if fallback to standard print is needed. + * Copy a potential Netlink message buffer from the traced process + * and attempt to decode and print it. */ static bool -print_netlink(FILE *fp, struct trussinfo *trussinfo, struct msghdr *msg) +print_netlink(FILE *fp, struct trussinfo *trussinfo, void *msg, size_t len) { - struct sockaddr_storage ss; - struct iovec iov; - struct ptrace_io_desc piod; char *buf; pid_t pid = trussinfo->curthread->proc->pid; bool success = false; - /* Only decode AF_NETLINK sockets. */ - if (msg->msg_name == NULL || msg->msg_namelen < offsetof(struct sockaddr, sa_data) - || msg->msg_iovlen == 0 || msg->msg_iov == NULL) + if (msg == NULL || len == 0) return (false); - if (get_struct(pid, (uintptr_t)msg->msg_name, &ss, - MIN(sizeof(ss), msg->msg_namelen)) == -1) - return (false); - - if (ss.ss_family != AF_NETLINK) - return (false); - - if (get_struct(pid, (uintptr_t)msg->msg_iov, &iov, sizeof(iov)) == -1) - return (false); - - /* Cap read size to avoid unbounded allocations. */ - size_t read_len = MIN(iov.iov_len, NETLINK_MAX_DECODE); + size_t read_len = MIN(len, NETLINK_MAX_DECODE); if (read_len == 0) return (false); @@ -1609,18 +1610,11 @@ print_netlink(FILE *fp, struct trussinfo *trussinfo, struct msghdr *msg) if (buf == NULL) return (false); - /* Snapshot User Memory using PTRACE. */ - piod.piod_op = PIOD_READ_D; - piod.piod_offs = iov.iov_base; - piod.piod_addr = buf; - piod.piod_len = read_len; - - if (ptrace(PT_IO, pid, (caddr_t)&piod, 0) == -1) { + if (get_struct(pid, (uintptr_t)msg, buf, read_len) == -1) { free(buf); return (false); } - /* Delegate Decoding to libsysdecode. */ if (sysdecode_netlink(fp, buf, read_len)) { success = true; } @@ -1637,7 +1631,7 @@ print_netlink(FILE *fp, struct trussinfo *trussinfo, struct msghdr *msg) */ char * print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval, - struct trussinfo *trussinfo) + struct trussinfo *trussinfo, struct syscall_decode *decode) { FILE *fp; char *tmp; @@ -1695,6 +1689,13 @@ print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval, break; } case BinString: { + if ((strcmp(decode->name, "sendto") == 0 || + strcmp(decode->name, "recvfrom") == 0) && + is_netlink(trussinfo, (int)args[0])) + if (print_netlink(fp, trussinfo, + (void *)args[sc->offset], + (size_t)args[sc->offset + 1])) + break; /* * Binary block of data that might have printable characters. * XXX If type|OUT, assume that the length is the syscall's @@ -2758,6 +2759,7 @@ print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval, } case Msghdr: { struct msghdr msghdr; + struct iovec iov; if (get_struct(pid, args[sc->offset], &msghdr, sizeof(struct msghdr)) == -1) { @@ -2768,7 +2770,11 @@ print_arg(struct syscall_arg *sc, syscallarg_t *args, syscallarg_t *retval, print_sockaddr(fp, trussinfo, (uintptr_t)msghdr.msg_name, msghdr.msg_namelen); fprintf(fp, ",%d,", msghdr.msg_namelen); /* Attempt Netlink decode; fallback to standard iovec if it fails. */ - if (!print_netlink(fp, trussinfo, &msghdr)) { + if ((!is_netlink(trussinfo, (int)args[0])) || + (get_struct(pid, (uintptr_t)msghdr.msg_iov, + &iov, sizeof(iov)) == -1) || + (!print_netlink(fp, trussinfo, (void *)iov.iov_base, + (size_t)iov.iov_len))) { print_iovec(fp, trussinfo, (uintptr_t)msghdr.msg_iov, msghdr.msg_iovlen); } diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h index e895ca27c26e..737ebc184806 100644 --- a/usr.bin/truss/truss.h +++ b/usr.bin/truss/truss.h @@ -94,12 +94,21 @@ struct threadinfo struct timespec after; }; +struct fd_domain +{ + LIST_ENTRY(fd_domain) entries; + + int fd; + int domain; +}; + struct procinfo { LIST_ENTRY(procinfo) entries; pid_t pid; struct procabi *abi; LIST_HEAD(, threadinfo) threadlist; + LIST_HEAD(, fd_domain) fdlist; }; struct trussinfo
