summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry V. Levin <ldv@strace.io>2021-05-02 08:00:00 +0000
committerDmitry V. Levin <ldv@strace.io>2021-05-02 08:00:00 +0000
commitbd93379586c4a117eb1e8dea88c64f39da4e2944 (patch)
tree47b5396a3fa1a949b720c71c183b19f81864eff6
parentf7c07acc1bef1b0682f0fc542bde44572f659a27 (diff)
downloadstrace-bd93379586c4a117eb1e8dea88c64f39da4e2944.tar.gz
netlink: validate struct nlmsgerr.msg.nlmsg_len
* src/netlink.c (decode_nlmsgerr): Use sizeof(struct nlmsgerr.msg) instead of struct nlmsgerr.msg.nlmsg_len if the latter is invalid. * tests/netlink_protocol.c (test_nlmsgerr): Check it.
-rw-r--r--src/netlink.c18
-rw-r--r--tests/netlink_protocol.c26
2 files changed, 40 insertions, 4 deletions
diff --git a/src/netlink.c b/src/netlink.c
index f5487fbad..cf7eee83d 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -533,10 +533,20 @@ decode_nlmsgerr(struct tcb *const tcp,
tprint_struct_next();
tprints_field_name("msg");
if (fetch_nlmsghdr(tcp, &err.msg, addr, len, false)) {
- unsigned int payload =
- capped ? sizeof(err.msg) : err.msg.nlmsg_len;
- if (payload > len)
- payload = len;
+ /*
+ * If err.msg.nlmsg_len < sizeof(err.msg), then it is
+ * invalid and sizeof(err.msg) would be used instead.
+ */
+ const unsigned int nlmsg_len =
+ MAX(sizeof(err.msg), err.msg.nlmsg_len);
+ /*
+ * Despite of fetch_nlmsghdr guarantee that
+ * len >= sizeof(err.msg)
+ * a valid nlmsg_len can exceed sizeof(err.msg)
+ * and an invalid nlmsg_len can exceed len.
+ */
+ const unsigned int payload =
+ MIN(len, capped ? sizeof(err.msg) : nlmsg_len);
decode_nlmsghdr_with_payload(tcp, fd, family,
&err.msg, addr, payload);
diff --git a/tests/netlink_protocol.c b/tests/netlink_protocol.c
index bb368cb19..9cc9535c8 100644
--- a/tests/netlink_protocol.c
+++ b/tests/netlink_protocol.c
@@ -285,6 +285,32 @@ test_nlmsgerr(const int fd)
err->msg.nlmsg_seq, err->msg.nlmsg_pid,
nlh->nlmsg_len, sprintrc(rc));
+ /* err->msg.nlmsg_len < sizeof(err->msg) */
+ err->msg.nlmsg_len = sizeof(err->msg.nlmsg_len);
+
+ rc = sendto(fd, nlh, nlh->nlmsg_len, MSG_DONTWAIT, NULL, 0);
+ printf("sendto(%d, [{nlmsg_len=%u, nlmsg_type=NLMSG_ERROR"
+ ", nlmsg_flags=NLM_F_REQUEST, nlmsg_seq=0, nlmsg_pid=0}"
+ ", {error=-EACCES, msg={nlmsg_len=%u, nlmsg_type=NLMSG_NOOP"
+ ", nlmsg_flags=NLM_F_REQUEST|0x%x, nlmsg_seq=%u, nlmsg_pid=%d}}]"
+ ", %u, MSG_DONTWAIT, NULL, 0) = %s\n",
+ fd, nlh->nlmsg_len, err->msg.nlmsg_len, NLM_F_DUMP,
+ err->msg.nlmsg_seq, err->msg.nlmsg_pid,
+ nlh->nlmsg_len, sprintrc(rc));
+
+ /* nlh->nlmsg_len < NLMSG_HDRLEN + err->msg.nlmsg_len */
+ err->msg.nlmsg_len = NLMSG_HDRLEN + 4;
+
+ rc = sendto(fd, nlh, nlh->nlmsg_len, MSG_DONTWAIT, NULL, 0);
+ printf("sendto(%d, [{nlmsg_len=%u, nlmsg_type=NLMSG_ERROR"
+ ", nlmsg_flags=NLM_F_REQUEST, nlmsg_seq=0, nlmsg_pid=0}"
+ ", {error=-EACCES, msg={nlmsg_len=%u, nlmsg_type=NLMSG_NOOP"
+ ", nlmsg_flags=NLM_F_REQUEST|0x%x, nlmsg_seq=%u, nlmsg_pid=%d}}]"
+ ", %u, MSG_DONTWAIT, NULL, 0) = %s\n",
+ fd, nlh->nlmsg_len, err->msg.nlmsg_len, NLM_F_DUMP,
+ err->msg.nlmsg_seq, err->msg.nlmsg_pid,
+ nlh->nlmsg_len, sprintrc(rc));
+
/* error message with room for the error code, a header, and some data */
nlh = nlh0 - sizeof(*err) - 4;
nlh->nlmsg_len = NLMSG_HDRLEN + sizeof(*err) + 4;