git: 6c744621c720 - main - truss: add AF_NETLINK syscall argument decoding support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 24 Jun 2026 14:50:22 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=6c744621c72062edf176ecf28b92ad10e8264454
commit 6c744621c72062edf176ecf28b92ad10e8264454
Author: Ishan Agrawal <iagrawal9990@gmail.com>
AuthorDate: 2026-06-24 05:20:15 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
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 <iagrawal9990@gmail.com>
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