[PATCH net-next v2 2/2] ipv4: igmp: convert ip_mc_msfget() to sockopt_t

From: Breno Leitao

Date: Mon Sep 14 2026 - 09:14:10 EST


IP_MSFILTER reads its reply through ip_mc_msfget(), reached from
do_ip_getsockopt() and from nowhere else. Convert it, and build the
sockopt_t at the call site for as long as the caller still carries a
sockptr_t pair.

optlen here only has to cover the header, and the real reply size comes
from the imsf_numsrc field inside it. This is nasty, but userspace
relies on it, so sockopt_expand_out() preserves the same mechanism: it
grows optval only for a user address, and assumes the caller left room
for the size its own header asked for.

The *optlen store moves out of ip_mc_msfget() and into the call site,
guarded by !err so the -EINVAL, -ENODEV and -EADDRNOTAVAIL returns still
leave the caller's optlen word untouched.

The source list also moves from copy_to_sockptr_offset() to a sequential
copy_to_iter(). IP_MSFILTER_SIZE(0) and offsetof(struct ip_msfilter,
imsf_slist_flex) are both 16, so the bytes land where they did.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
include/linux/igmp.h | 3 ++-
net/ipv4/igmp.c | 23 ++++++++++++++---------
net/ipv4/ip_sockglue.c | 10 +++++++++-
3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index a0cf0398519fd7..e075611344ef3b 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -14,6 +14,7 @@
#include <linux/timer.h>
#include <linux/in.h>
#include <linux/ip.h>
+#include <linux/net.h>
#include <linux/refcount.h>
#include <linux/sockptr.h>
#include <uapi/linux/igmp.h>
@@ -273,7 +274,7 @@ extern int ip_mc_source(int add, int omode, struct sock *sk,
struct ip_mreq_source *mreqs, int ifindex);
extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf,int ifindex);
extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
- sockptr_t optval, sockptr_t optlen);
+ sockopt_t *opt);
extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf,
sockptr_t optval, size_t offset);
extern int ip_mc_sf_allow(const struct sock *sk, __be32 local, __be32 rmt,
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index d56355aca79776..144fca158adcb0 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2709,8 +2709,8 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
err = ip_mc_leave_group(sk, &imr);
return err;
}
-int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
- sockptr_t optval, sockptr_t optlen)
+
+int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, sockopt_t *opt)
{
int err, len, count, copycount, msf_size;
struct ip_mreqn imr;
@@ -2755,14 +2755,19 @@ int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
len = flex_array_size(psl, sl_addr, copycount);
msf->imsf_numsrc = count;
msf_size = IP_MSFILTER_SIZE(copycount);
- if (copy_to_sockptr(optlen, &msf_size, sizeof(int)) ||
- copy_to_sockptr(optval, msf, IP_MSFILTER_SIZE(0))) {
+
+ /* The source list is sized by the imsf_numsrc the caller left in
+ * optval, not by optlen, which only has to cover the fixed part.
+ */
+ err = sockopt_expand_out(opt, msf_size);
+ if (err)
+ return err;
+
+ opt->optlen = msf_size;
+ if (copy_to_iter(msf, IP_MSFILTER_SIZE(0), &opt->iter_out) !=
+ IP_MSFILTER_SIZE(0))
return -EFAULT;
- }
- if (len &&
- copy_to_sockptr_offset(optval,
- offsetof(struct ip_msfilter, imsf_slist_flex),
- psl->sl_addr, len))
+ if (len && copy_to_iter(psl->sl_addr, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
done:
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index a55ef327ec932c..e06c1f48ecad6e 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1706,6 +1706,8 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
case IP_MSFILTER:
{
struct ip_msfilter msf;
+ struct kvec kvec;
+ sockopt_t opt;

if (len < IP_MSFILTER_SIZE(0)) {
err = -EINVAL;
@@ -1715,7 +1717,13 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
err = -EFAULT;
goto out;
}
- err = ip_mc_msfget(sk, &msf, optval, optlen);
+ err = sockptr_to_sockopt(&opt, optval, optlen, &kvec);
+ if (err)
+ goto out;
+
+ err = ip_mc_msfget(sk, &msf, &opt);
+ if (!err && copy_to_sockptr(optlen, &opt.optlen, sizeof(int)))
+ err = -EFAULT;
goto out;
}
case MCAST_MSFILTER:

--
2.53.0-Meta