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

Reply via email to