git: 2780e5f43d5b - main - linux: allow RTM_GETADDR without full ifaddrmsg argument

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Tue, 28 May 2024 20:13:17 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=2780e5f43d5b0e8b155472300ee63816a660780e

commit 2780e5f43d5b0e8b155472300ee63816a660780e
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2024-05-28 20:13:08 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2024-05-28 20:13:08 +0000

    linux: allow RTM_GETADDR without full ifaddrmsg argument
    
    Even modern glibc uses truncated argument for RTM_GETADDR when it wants to
    list all addresses in a system.  See
    sysdeps/unix/sysv/linux/ifaddrs.c:__netlink_sendreq().  It sends a one
    char payload.  Linux kernel allows that as long as given socket is not
    marked as a 'strict'.  We have a similar flag in the general netlink code
    and it is checked in
    sys/netlink/netlink_message_parser.h:nl_parse_header().  If the flag is
    not present, parser will allocate a temporary zeroed buffer to make the
    message correct.  The checks added in b977dd1ea5fb blocked such message
    before the parser.  My reading of glibc says that there are two types of
    messages that are sent with __netlink_sendreq() - RTM_GETLINK and
    RTM_GETADDR.  The RTM_GETLINK is binary compatible between Linux and
    FreeBSD and thus doesn't need any ABI handler.
    
    PR:             279012
    Fixes:          b977dd1ea5fbc2df3f1279330be4d089322eb2cf
---
 sys/compat/linux/linux_netlink.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sys/compat/linux/linux_netlink.c b/sys/compat/linux/linux_netlink.c
index 8675f830b4ef..f51838ee00d7 100644
--- a/sys/compat/linux/linux_netlink.c
+++ b/sys/compat/linux/linux_netlink.c
@@ -94,7 +94,8 @@ rtnl_ifaddr_from_linux(struct nlmsghdr *hdr, struct nl_pstate *npt)
 	struct ifaddrmsg *ifam = (struct ifaddrmsg *)(hdr + 1);
 	sa_family_t f;
 
-	if (hdr->nlmsg_len < sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg))
+	if (hdr->nlmsg_len < sizeof(struct nlmsghdr) +
+	    offsetof(struct ifaddrmsg, ifa_family) + sizeof(ifam->ifa_family))
 		return (EBADMSG);
 	if ((f = linux_to_bsd_domain(ifam->ifa_family)) == AF_UNKNOWN)
 		return (EPFNOSUPPORT);