diff options
| author | Gleb Smirnoff <glebius@FreeBSD.org> | 2026-05-20 14:27:39 +0000 |
|---|---|---|
| committer | Gleb Smirnoff <glebius@FreeBSD.org> | 2026-05-20 14:27:39 +0000 |
| commit | e145afc9eb9187be7e7cfdd42043cefc07b6e8d6 (patch) | |
| tree | 868985165acbe6889c61a4b71c98c9f8e3754613 | |
| parent | d9c31658df8492a5666e799c2b267c425cb8f468 (diff) | |
| -rw-r--r-- | sys/netlink/netlink_message_parser.c | 32 | ||||
| -rw-r--r-- | sys/netlink/netlink_message_parser.h | 29 |
2 files changed, 40 insertions, 21 deletions
diff --git a/sys/netlink/netlink_message_parser.c b/sys/netlink/netlink_message_parser.c index 4c41235efaac..54aceb660a98 100644 --- a/sys/netlink/netlink_message_parser.c +++ b/sys/netlink/netlink_message_parser.c @@ -50,6 +50,38 @@ #include <netlink/netlink_debug.h> _DECLARE_DEBUG(LOG_INFO); +/* + * Some applications try to provide only the non-zero part of the required + * message header instead of a full one. It happens when fetching routes or + * interface addresses, where the first header byte is the family. + * This behavior is "illegal" under the "strict" Netlink socket option, however + * there are many applications out there doing things in the "old" way. + * Support this usecase by copying the provided bytes into the temporary + * zero-filled header and running the parser on this header instead. + */ +struct nlmsghdr * +nl_alloc_compat_hdr(struct nlmsghdr *hdr, uint32_t len, struct nl_pstate *npt) +{ + struct nlmsghdr *tmp; + + MPASS(hdr->nlmsg_len < sizeof(struct nlmsghdr) + len); + + len += sizeof(struct nlmsghdr); + if (npt->strict) { + nlmsg_report_err_msg(npt, + "header too short: expected %d, got %d", + len, hdr->nlmsg_len); + return (NULL); + } + tmp = npt_alloc(npt, len); + if (tmp == NULL) + return (NULL); + memcpy(tmp, hdr, hdr->nlmsg_len); + tmp->nlmsg_len = len; + + return (tmp); +} + bool nlmsg_report_err_msg(struct nl_pstate *npt, const char *fmt, ...) { diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h index 720317ed74f3..8f61091c4a7f 100644 --- a/sys/netlink/netlink_message_parser.h +++ b/sys/netlink/netlink_message_parser.h @@ -222,6 +222,9 @@ bool nlmsg_report_err_offset(struct nl_pstate *npt, uint32_t off); void nlmsg_report_cookie(struct nl_pstate *npt, struct nlattr *nla); void nlmsg_report_cookie_u32(struct nl_pstate *npt, uint32_t val); +struct nlmsghdr *nl_alloc_compat_hdr(struct nlmsghdr *hdr, uint32_t len, + struct nl_pstate *npt); + /* * Have it inline so compiler can optimize field accesses into * the list of direct function calls without iteration. @@ -232,27 +235,7 @@ nl_parse_header(void *hdr, uint32_t len, const struct nlhdr_parser *parser, { int error; - if (__predict_false(len < parser->nl_hdr_off)) { - void *tmp_hdr; - - if (npt->strict) { - nlmsg_report_err_msg(npt, - "header too short: expected %d, got %d", - parser->nl_hdr_off, len); - return (EINVAL); - } - - /* - * Compatibility with older applications: - * pretend there's a full header. - */ - tmp_hdr = npt_alloc(npt, parser->nl_hdr_off); - if (tmp_hdr == NULL) - return (EINVAL); - memcpy(tmp_hdr, hdr, len); - hdr = tmp_hdr; - len = parser->nl_hdr_off; - } + MPASS(len >= parser->nl_hdr_off); if (npt->strict && parser->sp != NULL && !parser->sp(hdr, npt)) return (EINVAL); @@ -320,6 +303,10 @@ static inline int nl_parse_nlmsg(struct nlmsghdr *hdr, const struct nlhdr_parser *parser, struct nl_pstate *npt, void *target) { + if (__predict_false(hdr->nlmsg_len - sizeof(struct nlmsghdr) < + parser->nl_hdr_off) && + ((hdr = nl_alloc_compat_hdr(hdr, parser->nl_hdr_off, npt)) == NULL)) + return (ENOMEM); return (nl_parse_header(hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, npt, target)); } |
