* [PATCH net-next v3 0/2] bridge: report an oversized IFLA_AF_SPEC nest instead of truncating
@ 2026-09-19 13:43 Artem Lytkin
2026-09-19 13:43 ` [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink() Artem Lytkin
2026-09-19 13:43 ` [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute Artem Lytkin
0 siblings, 2 replies; 7+ messages in thread
From: Artem Lytkin @ 2026-09-19 13:43 UTC (permalink / raw)
To: netdev
Cc: bridge, razor, idosch, davem, edumazet, kuba, pabeni, horms,
andrew+netdev, kuniyu, michael.chan, pavan.chebbi, ajit.khaparde,
sriharsha.basavapatna, anthony.l.nguyen, przemyslaw.kitszel,
intel-wired-lan, saeedm, tariqt, mbloch, oss-drivers, wintera,
aswin, linux-s390
Patch 1 adds extack to ndo_bridge_getlink(), patch 2 returns -E2BIG
when the AF_SPEC nest, or the MST/CFM nest inside it, overflows nla_len.
v3: handle the MST and CFM nests too, use nla_nest_end_safe() (Nikolay)
v2: https://lore.kernel.org/netdev/20260912135022.1701-1-iprintercanon@gmail.com/
v1: https://lore.kernel.org/netdev/20260906224246.21719-1-iprintercanon@gmail.com/
Artem Lytkin (2):
rtnetlink: pass extack to ndo_bridge_getlink()
net: bridge: fail link info that does not fit in a netlink attribute
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 +--
drivers/net/ethernet/emulex/benet/be_main.c | 4 +--
drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++--
drivers/net/ethernet/intel/ice/ice_main.c | 6 ++--
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 5 ++--
.../net/ethernet/mellanox/mlx5/core/en_main.c | 4 +--
.../ethernet/netronome/nfp/nfp_net_common.c | 4 +--
drivers/s390/net/qeth_l2_main.c | 4 +--
include/linux/netdevice.h | 5 ++--
include/linux/rtnetlink.h | 3 +-
net/bridge/br_netlink.c | 28 +++++++++++++------
net/bridge/br_private.h | 2 +-
net/core/rtnetlink.c | 18 +++++++-----
13 files changed, 57 insertions(+), 36 deletions(-)
base-commit: e3bfd25626b44b6fa61a13c17178922171d519ce
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink()
2026-09-19 13:43 [PATCH net-next v3 0/2] bridge: report an oversized IFLA_AF_SPEC nest instead of truncating Artem Lytkin
@ 2026-09-19 13:43 ` Artem Lytkin
2026-09-20 7:08 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
2026-09-19 13:43 ` [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute Artem Lytkin
1 sibling, 2 replies; 7+ messages in thread
From: Artem Lytkin @ 2026-09-19 13:43 UTC (permalink / raw)
To: netdev
Cc: bridge, razor, idosch, davem, edumazet, kuba, pabeni, horms,
andrew+netdev, kuniyu, michael.chan, pavan.chebbi, ajit.khaparde,
sriharsha.basavapatna, anthony.l.nguyen, przemyslaw.kitszel,
intel-wired-lan, saeedm, tariqt, mbloch, oss-drivers, wintera,
aswin, linux-s390
So the bridge can say why filling the link info failed.
No functional change.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Artem Lytkin <iprintercanon@gmail.com>
---
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++--
drivers/net/ethernet/emulex/benet/be_main.c | 4 ++--
drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++++--
drivers/net/ethernet/intel/ice/ice_main.c | 6 ++++--
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 5 +++--
.../net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++--
.../ethernet/netronome/nfp/nfp_net_common.c | 4 ++--
drivers/s390/net/qeth_l2_main.c | 4 ++--
include/linux/netdevice.h | 5 +++--
include/linux/rtnetlink.h | 3 ++-
net/bridge/br_netlink.c | 3 ++-
net/bridge/br_private.h | 2 +-
net/core/rtnetlink.c | 18 +++++++++++-------
13 files changed, 40 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index ca99f4b1a63c3..a752e66bf6368 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -16154,12 +16154,12 @@ static const struct udp_tunnel_nic_info bnxt_udp_tunnels = {
static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
- int nlflags)
+ int nlflags, struct netlink_ext_ack *extack)
{
struct bnxt *bp = netdev_priv(dev);
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bp->br_mode, 0, 0,
- nlflags, filter_mask, NULL);
+ nlflags, filter_mask, NULL, extack);
}
static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index ed302f5ec4768..cc9c87539a429 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -5011,7 +5011,7 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
- int nlflags)
+ int nlflags, struct netlink_ext_ack *extack)
{
struct be_adapter *adapter = netdev_priv(dev);
int status = 0;
@@ -5037,7 +5037,7 @@ static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
hsw_mode == PORT_FWD_TYPE_VEPA ?
BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB,
- 0, 0, nlflags, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL, extack);
}
static struct be_cmd_work *be_alloc_work(struct be_adapter *adapter,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index abbc71e815ae3..52c2b393aacd2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -13151,6 +13151,7 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
* @dev: the netdev being configured
* @filter_mask: unused
* @nlflags: netlink flags passed in
+ * @extack: netlink extended ack
*
* Return the mode in which the hardware bridge is operating in
* i.e VEB or VEPA.
@@ -13158,7 +13159,8 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
u32 __always_unused filter_mask,
- int nlflags)
+ int nlflags,
+ struct netlink_ext_ack *extack)
{
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;
@@ -13175,7 +13177,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
- 0, 0, nlflags, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL, extack);
}
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 707c7431b91a6..f98295438df78 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -8095,12 +8095,14 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc)
* @dev: the netdev being configured
* @filter_mask: filter mask passed in
* @nlflags: netlink flags passed in
+ * @extack: netlink extended ack
*
* Return the bridge mode (VEB/VEPA)
*/
static int
ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
- struct net_device *dev, u32 filter_mask, int nlflags)
+ struct net_device *dev, u32 filter_mask, int nlflags,
+ struct netlink_ext_ack *extack)
{
struct ice_pf *pf = ice_netdev_to_pf(dev);
u16 bmode;
@@ -8108,7 +8110,7 @@ ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
bmode = pf->first_sw->bridge_mode;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bmode, 0, 0, nlflags,
- filter_mask, NULL);
+ filter_mask, NULL, extack);
}
/**
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index f91856498eb2d..77dfd80a6e368 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -10726,7 +10726,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev,
- u32 filter_mask, int nlflags)
+ u32 filter_mask, int nlflags,
+ struct netlink_ext_ack *extack)
{
struct ixgbe_adapter *adapter = ixgbe_from_netdev(dev);
@@ -10735,7 +10736,7 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
adapter->bridge_mode, 0, 0, nlflags,
- filter_mask, NULL);
+ filter_mask, NULL, extack);
}
static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index fc110a7d16e8d..53bf79dbe08de 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -5277,7 +5277,7 @@ static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp)
#ifdef CONFIG_MLX5_ESWITCH
static int mlx5e_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
- int nlflags)
+ int nlflags, struct netlink_ext_ack *extack)
{
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5_core_dev *mdev = priv->mdev;
@@ -5288,7 +5288,7 @@ static int mlx5e_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
mode = setting ? BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
mode,
- 0, 0, nlflags, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL, extack);
}
static int mlx5e_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 7928e76da723e..cb751dddb6887 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -2257,7 +2257,7 @@ static int nfp_net_set_mac_address(struct net_device *netdev, void *addr)
static int nfp_net_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
- int nlflags)
+ int nlflags, struct netlink_ext_ack *extack)
{
struct nfp_net *nn = netdev_priv(dev);
u16 mode;
@@ -2269,7 +2269,7 @@ static int nfp_net_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 0, 0,
- nlflags, filter_mask, NULL);
+ nlflags, filter_mask, NULL, extack);
}
static int nfp_net_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 2935c2ecc314b..f960cf5a97a29 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -935,7 +935,7 @@ static void qeth_l2_br2dev_put(void)
static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask,
- int nlflags)
+ int nlflags, struct netlink_ext_ack *extack)
{
struct qeth_priv *priv = netdev_priv(dev);
struct qeth_card *card = dev->ml_priv;
@@ -949,7 +949,7 @@ static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
mode, priv->brport_features,
priv->brport_hw_features,
- nlflags, filter_mask, NULL);
+ nlflags, filter_mask, NULL, extack);
}
static const struct nla_policy qeth_brport_policy[IFLA_BRPORT_MAX + 1] = {
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 1f0710eef185b..02238b1e33453 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1360,7 +1360,7 @@ struct netdev_net_notifier {
* u16 flags, struct netlink_ext_ack *extack)
* int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
* struct net_device *dev, u32 filter_mask,
- * int nlflags)
+ * int nlflags, struct netlink_ext_ack *extack)
* int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
* u16 flags);
*
@@ -1659,7 +1659,8 @@ struct net_device_ops {
u32 pid, u32 seq,
struct net_device *dev,
u32 filter_mask,
- int nlflags);
+ int nlflags,
+ struct netlink_ext_ack *extack);
int (*ndo_bridge_dellink)(struct net_device *dev,
struct nlmsghdr *nlh,
u16 flags);
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 95729339e7a54..a408c36a7b558 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -222,7 +222,8 @@ extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
u32 filter_mask,
int (*vlan_fill)(struct sk_buff *skb,
struct net_device *dev,
- u32 filter_mask));
+ u32 filter_mask),
+ struct netlink_ext_ack *extack);
extern void rtnl_offload_xstats_notify(struct net_device *dev);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index ae76df0de05a0..855a46aec3a89 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -680,7 +680,8 @@ void br_ifinfo_notify(int event, const struct net_bridge *br,
* Dump information about all ports, in response to GETLINK
*/
int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
- struct net_device *dev, u32 filter_mask, int nlflags)
+ struct net_device *dev, u32 filter_mask, int nlflags,
+ struct netlink_ext_ack *extack)
{
struct net_bridge_port *port = br_port_get_rtnl(dev);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 09c397e303307..edbe7a1f57363 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -2179,7 +2179,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags,
struct netlink_ext_ack *extack);
int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
- u32 filter_mask, int nlflags);
+ u32 filter_mask, int nlflags, struct netlink_ext_ack *extack);
int br_process_vlan_info(struct net_bridge *br,
struct net_bridge_port *p, int cmd,
struct bridge_vlan_info *vinfo_curr,
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e3444fd240615..a341e06f47d31 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -5420,7 +5420,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
u32 filter_mask,
int (*vlan_fill)(struct sk_buff *skb,
struct net_device *dev,
- u32 filter_mask))
+ u32 filter_mask),
+ struct netlink_ext_ack *extack)
{
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
@@ -5596,7 +5597,8 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
if (idx >= cb->args[0]) {
err = br_dev->netdev_ops->ndo_bridge_getlink(
skb, portid, seq, dev,
- filter_mask, NLM_F_MULTI);
+ filter_mask, NLM_F_MULTI,
+ cb->extack);
if (err < 0 && err != -EOPNOTSUPP) {
if (likely(skb->len))
break;
@@ -5612,7 +5614,8 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
err = ops->ndo_bridge_getlink(skb, portid,
seq, dev,
filter_mask,
- NLM_F_MULTI);
+ NLM_F_MULTI,
+ cb->extack);
if (err < 0 && err != -EOPNOTSUPP) {
if (likely(skb->len))
break;
@@ -5646,7 +5649,8 @@ static inline size_t bridge_nlmsg_size(void)
+ nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */
}
-static int rtnl_bridge_notify(struct net_device *dev)
+static int rtnl_bridge_notify(struct net_device *dev,
+ struct netlink_ext_ack *extack)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
@@ -5661,7 +5665,7 @@ static int rtnl_bridge_notify(struct net_device *dev)
goto errout;
}
- err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0, 0);
+ err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0, 0, extack);
if (err < 0)
goto errout;
@@ -5752,7 +5756,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
/* Generate event to notify upper layer of bridge
* change
*/
- err = rtnl_bridge_notify(dev);
+ err = rtnl_bridge_notify(dev, extack);
}
}
@@ -5827,7 +5831,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
/* Generate event to notify upper layer of bridge
* change
*/
- err = rtnl_bridge_notify(dev);
+ err = rtnl_bridge_notify(dev, extack);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute
2026-09-19 13:43 [PATCH net-next v3 0/2] bridge: report an oversized IFLA_AF_SPEC nest instead of truncating Artem Lytkin
2026-09-19 13:43 ` [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink() Artem Lytkin
@ 2026-09-19 13:43 ` Artem Lytkin
2026-09-20 7:06 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
1 sibling, 2 replies; 7+ messages in thread
From: Artem Lytkin @ 2026-09-19 13:43 UTC (permalink / raw)
To: netdev
Cc: bridge, razor, idosch, davem, edumazet, kuba, pabeni, horms,
andrew+netdev, kuniyu, michael.chan, pavan.chebbi, ajit.khaparde,
sriharsha.basavapatna, anthony.l.nguyen, przemyslaw.kitszel,
intel-wired-lan, saeedm, tariqt, mbloch, oss-drivers, wintera,
aswin, linux-s390
nla_len is a u16, and a port with enough VLANs, tunnels or MST entries
makes the IFLA_AF_SPEC nest wrap, so userspace reads garbage. Same for
the inner MST and CFM nests.
Use nla_nest_end_safe() for all three and return -E2BIG with an extack
on overflow. Dumps end with that error, notifications go through
rtnl_set_sk_err() so listeners resync.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Artem Lytkin <iprintercanon@gmail.com>
---
net/bridge/br_netlink.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 855a46aec3a89..fbd91b86d4268 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -459,7 +459,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
const struct net_bridge_port *port,
u32 pid, u32 seq, int event, unsigned int flags,
u32 filter_mask, const struct net_device *dev,
- bool getlink)
+ bool getlink, struct netlink_ext_ack *extack)
{
u8 operstate = netif_running(dev) ? READ_ONCE(dev->operstate) :
IF_OPER_DOWN;
@@ -588,7 +588,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
goto nla_put_failure;
}
- nla_nest_end(skb, cfm_nest);
+ if (nla_nest_end_safe(skb, cfm_nest) < 0)
+ goto nla_nest_too_large;
}
if ((filter_mask & RTEXT_FILTER_MST) &&
@@ -608,20 +609,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
if (err)
goto nla_put_failure;
- nla_nest_end(skb, mst_nest);
+ if (nla_nest_end_safe(skb, mst_nest) < 0)
+ goto nla_nest_too_large;
}
done:
if (af) {
- if (nlmsg_get_pos(skb) - (void *)af > nla_attr_size(0))
- nla_nest_end(skb, af);
- else
+ if (nla_nest_end_safe(skb, af) < 0)
+ goto nla_nest_too_large;
+ if (!nla_len(af))
nla_nest_cancel(skb, af);
}
nlmsg_end(skb, nlh);
return 0;
+nla_nest_too_large:
+ NL_SET_ERR_MSG_MOD(extack,
+ "AF_SPEC info too large, use per-object dumps (e.g. RTM_GETVLAN)");
+ nlmsg_cancel(skb, nlh);
+ return -E2BIG;
+
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
@@ -654,7 +662,8 @@ void br_info_notify(int event, const struct net_bridge *br,
if (skb == NULL)
goto errout;
- err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false);
+ err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false,
+ NULL);
if (err < 0) {
/* -EMSGSIZE implies BUG in br_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -693,7 +702,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;
return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
- filter_mask, dev, true);
+ filter_mask, dev, true, extack);
}
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute
2026-09-19 13:43 ` [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute Artem Lytkin
@ 2026-09-20 7:06 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
1 sibling, 0 replies; 7+ messages in thread
From: Nikolay Aleksandrov @ 2026-09-20 7:06 UTC (permalink / raw)
To: Artem Lytkin, netdev
Cc: bridge, idosch, davem, edumazet, kuba, pabeni, horms,
andrew+netdev, kuniyu, michael.chan, pavan.chebbi, ajit.khaparde,
sriharsha.basavapatna, anthony.l.nguyen, przemyslaw.kitszel,
intel-wired-lan, saeedm, tariqt, mbloch, oss-drivers, wintera,
aswin, linux-s390
On 19/09/2026 16:43, Artem Lytkin wrote:
> nla_len is a u16, and a port with enough VLANs, tunnels or MST entries
> makes the IFLA_AF_SPEC nest wrap, so userspace reads garbage. Same for
> the inner MST and CFM nests.
>
> Use nla_nest_end_safe() for all three and return -E2BIG with an extack
> on overflow. Dumps end with that error, notifications go through
> rtnl_set_sk_err() so listeners resync.
>
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: Artem Lytkin <iprintercanon@gmail.com>
> ---
> net/bridge/br_netlink.c | 25 +++++++++++++++++--------
> 1 file changed, 17 insertions(+), 8 deletions(-)
>
You can add specific extack messages to the different dumping parts of br_fill_ifinfo
so the user can identify exactly which one doesn't fit and get a more accurate error.
More below...
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 855a46aec3a89..fbd91b86d4268 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -459,7 +459,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> const struct net_bridge_port *port,
> u32 pid, u32 seq, int event, unsigned int flags,
> u32 filter_mask, const struct net_device *dev,
> - bool getlink)
> + bool getlink, struct netlink_ext_ack *extack)
> {
> u8 operstate = netif_running(dev) ? READ_ONCE(dev->operstate) :
> IF_OPER_DOWN;
> @@ -588,7 +588,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> goto nla_put_failure;
> }
>
Before these you can call if (nla_nest_end_safe(skb, af) < 0) { }
in the vlan block and set a vlan-specific extack error message, in fact
you can do that after vlan info is dumped, then after vlan tunnels are dumped
and set specific messages depending on which one didn't fit.
nla_nest_end_safe updates nla_len but you can still append attributes
after it has been called
Then you have MRP, you can do the same there and so on.
> - nla_nest_end(skb, cfm_nest);
> + if (nla_nest_end_safe(skb, cfm_nest) < 0)
> + goto nla_nest_too_large;
This can set its own extack err message, e.g.
CFM information exceeds the netlink attribute size limit
> }
>
> if ((filter_mask & RTEXT_FILTER_MST) &&
> @@ -608,20 +609,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> if (err)
> goto nla_put_failure;
>
> - nla_nest_end(skb, mst_nest);
> + if (nla_nest_end_safe(skb, mst_nest) < 0)
> + goto nla_nest_too_large;
Same here but with MST
> }
>
> done:
> if (af) {
> - if (nlmsg_get_pos(skb) - (void *)af > nla_attr_size(0))
> - nla_nest_end(skb, af);
> - else
> + if (nla_nest_end_safe(skb, af) < 0)
> + goto nla_nest_too_large;
> + if (!nla_len(af))
> nla_nest_cancel(skb, af);
> }
>
> nlmsg_end(skb, nlh);
> return 0;
>
> +nla_nest_too_large:
> + NL_SET_ERR_MSG_MOD(extack,
> + "AF_SPEC info too large, use per-object dumps (e.g. RTM_GETVLAN)");
Just make sure here to use NL_SET_ERR_MSG_WEAK_MOD() so extack doesn't get overwritten
> + nlmsg_cancel(skb, nlh);
> + return -E2BIG;
> +
> nla_put_failure:
> nlmsg_cancel(skb, nlh);
> return -EMSGSIZE;
> @@ -654,7 +662,8 @@ void br_info_notify(int event, const struct net_bridge *br,
> if (skb == NULL)
> goto errout;
>
> - err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false);
> + err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false,
> + NULL);
> if (err < 0) {
> /* -EMSGSIZE implies BUG in br_nlmsg_size() */
> WARN_ON(err == -EMSGSIZE);
> @@ -693,7 +702,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> return 0;
>
> return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
> - filter_mask, dev, true);
> + filter_mask, dev, true, extack);
> }
>
> static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink()
2026-09-19 13:43 ` [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink() Artem Lytkin
@ 2026-09-20 7:08 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
1 sibling, 0 replies; 7+ messages in thread
From: Nikolay Aleksandrov @ 2026-09-20 7:08 UTC (permalink / raw)
To: Artem Lytkin, netdev
Cc: bridge, idosch, davem, edumazet, kuba, pabeni, horms,
andrew+netdev, kuniyu, michael.chan, pavan.chebbi, ajit.khaparde,
sriharsha.basavapatna, anthony.l.nguyen, przemyslaw.kitszel,
intel-wired-lan, saeedm, tariqt, mbloch, oss-drivers, wintera,
aswin, linux-s390
On 19/09/2026 16:43, Artem Lytkin wrote:
> So the bridge can say why filling the link info failed.
> No functional change.
>
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: Artem Lytkin <iprintercanon@gmail.com>
> ---
> drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++--
> drivers/net/ethernet/emulex/benet/be_main.c | 4 ++--
> drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++++--
> drivers/net/ethernet/intel/ice/ice_main.c | 6 ++++--
> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 5 +++--
> .../net/ethernet/mellanox/mlx5/core/en_main.c | 4 ++--
> .../ethernet/netronome/nfp/nfp_net_common.c | 4 ++--
> drivers/s390/net/qeth_l2_main.c | 4 ++--
> include/linux/netdevice.h | 5 +++--
> include/linux/rtnetlink.h | 3 ++-
> net/bridge/br_netlink.c | 3 ++-
> net/bridge/br_private.h | 2 +-
> net/core/rtnetlink.c | 18 +++++++++++-------
> 13 files changed, 40 insertions(+), 28 deletions(-)
>
Why do you add extack to ndo_dflt_bridge_getlink() at all? It doesn't use it and
just causes unnecessary churn in drivers.
> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> index ca99f4b1a63c3..a752e66bf6368 100644
> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> @@ -16154,12 +16154,12 @@ static const struct udp_tunnel_nic_info bnxt_udp_tunnels = {
>
> static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev, u32 filter_mask,
> - int nlflags)
> + int nlflags, struct netlink_ext_ack *extack)
> {
> struct bnxt *bp = netdev_priv(dev);
>
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bp->br_mode, 0, 0,
> - nlflags, filter_mask, NULL);
> + nlflags, filter_mask, NULL, extack);
> }
>
> static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
> diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
> index ed302f5ec4768..cc9c87539a429 100644
> --- a/drivers/net/ethernet/emulex/benet/be_main.c
> +++ b/drivers/net/ethernet/emulex/benet/be_main.c
> @@ -5011,7 +5011,7 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
>
> static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev, u32 filter_mask,
> - int nlflags)
> + int nlflags, struct netlink_ext_ack *extack)
> {
> struct be_adapter *adapter = netdev_priv(dev);
> int status = 0;
> @@ -5037,7 +5037,7 @@ static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
> hsw_mode == PORT_FWD_TYPE_VEPA ?
> BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB,
> - 0, 0, nlflags, filter_mask, NULL);
> + 0, 0, nlflags, filter_mask, NULL, extack);
> }
>
> static struct be_cmd_work *be_alloc_work(struct be_adapter *adapter,
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
> index abbc71e815ae3..52c2b393aacd2 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
> @@ -13151,6 +13151,7 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
> * @dev: the netdev being configured
> * @filter_mask: unused
> * @nlflags: netlink flags passed in
> + * @extack: netlink extended ack
> *
> * Return the mode in which the hardware bridge is operating in
> * i.e VEB or VEPA.
> @@ -13158,7 +13159,8 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
> static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev,
> u32 __always_unused filter_mask,
> - int nlflags)
> + int nlflags,
> + struct netlink_ext_ack *extack)
> {
> struct i40e_netdev_priv *np = netdev_priv(dev);
> struct i40e_vsi *vsi = np->vsi;
> @@ -13175,7 +13177,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> return 0;
>
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
> - 0, 0, nlflags, filter_mask, NULL);
> + 0, 0, nlflags, filter_mask, NULL, extack);
> }
>
> /**
> diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
> index 707c7431b91a6..f98295438df78 100644
> --- a/drivers/net/ethernet/intel/ice/ice_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_main.c
> @@ -8095,12 +8095,14 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc)
> * @dev: the netdev being configured
> * @filter_mask: filter mask passed in
> * @nlflags: netlink flags passed in
> + * @extack: netlink extended ack
> *
> * Return the bridge mode (VEB/VEPA)
> */
> static int
> ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> - struct net_device *dev, u32 filter_mask, int nlflags)
> + struct net_device *dev, u32 filter_mask, int nlflags,
> + struct netlink_ext_ack *extack)
> {
> struct ice_pf *pf = ice_netdev_to_pf(dev);
> u16 bmode;
> @@ -8108,7 +8110,7 @@ ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> bmode = pf->first_sw->bridge_mode;
>
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bmode, 0, 0, nlflags,
> - filter_mask, NULL);
> + filter_mask, NULL, extack);
> }
>
> /**
> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> index f91856498eb2d..77dfd80a6e368 100644
> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
> @@ -10726,7 +10726,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
>
> static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev,
> - u32 filter_mask, int nlflags)
> + u32 filter_mask, int nlflags,
> + struct netlink_ext_ack *extack)
> {
> struct ixgbe_adapter *adapter = ixgbe_from_netdev(dev);
>
> @@ -10735,7 +10736,7 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
>
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
> adapter->bridge_mode, 0, 0, nlflags,
> - filter_mask, NULL);
> + filter_mask, NULL, extack);
> }
>
> static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> index fc110a7d16e8d..53bf79dbe08de 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> @@ -5277,7 +5277,7 @@ static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp)
> #ifdef CONFIG_MLX5_ESWITCH
> static int mlx5e_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev, u32 filter_mask,
> - int nlflags)
> + int nlflags, struct netlink_ext_ack *extack)
> {
> struct mlx5e_priv *priv = netdev_priv(dev);
> struct mlx5_core_dev *mdev = priv->mdev;
> @@ -5288,7 +5288,7 @@ static int mlx5e_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> mode = setting ? BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB;
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
> mode,
> - 0, 0, nlflags, filter_mask, NULL);
> + 0, 0, nlflags, filter_mask, NULL, extack);
> }
>
> static int mlx5e_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
> diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
> index 7928e76da723e..cb751dddb6887 100644
> --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
> +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
> @@ -2257,7 +2257,7 @@ static int nfp_net_set_mac_address(struct net_device *netdev, void *addr)
>
> static int nfp_net_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev, u32 filter_mask,
> - int nlflags)
> + int nlflags, struct netlink_ext_ack *extack)
> {
> struct nfp_net *nn = netdev_priv(dev);
> u16 mode;
> @@ -2269,7 +2269,7 @@ static int nfp_net_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB;
>
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 0, 0,
> - nlflags, filter_mask, NULL);
> + nlflags, filter_mask, NULL, extack);
> }
>
> static int nfp_net_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
> diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
> index 2935c2ecc314b..f960cf5a97a29 100644
> --- a/drivers/s390/net/qeth_l2_main.c
> +++ b/drivers/s390/net/qeth_l2_main.c
> @@ -935,7 +935,7 @@ static void qeth_l2_br2dev_put(void)
>
> static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> struct net_device *dev, u32 filter_mask,
> - int nlflags)
> + int nlflags, struct netlink_ext_ack *extack)
> {
> struct qeth_priv *priv = netdev_priv(dev);
> struct qeth_card *card = dev->ml_priv;
> @@ -949,7 +949,7 @@ static int qeth_l2_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
> mode, priv->brport_features,
> priv->brport_hw_features,
> - nlflags, filter_mask, NULL);
> + nlflags, filter_mask, NULL, extack);
> }
>
> static const struct nla_policy qeth_brport_policy[IFLA_BRPORT_MAX + 1] = {
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 1f0710eef185b..02238b1e33453 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1360,7 +1360,7 @@ struct netdev_net_notifier {
> * u16 flags, struct netlink_ext_ack *extack)
> * int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
> * struct net_device *dev, u32 filter_mask,
> - * int nlflags)
> + * int nlflags, struct netlink_ext_ack *extack)
> * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
> * u16 flags);
> *
> @@ -1659,7 +1659,8 @@ struct net_device_ops {
> u32 pid, u32 seq,
> struct net_device *dev,
> u32 filter_mask,
> - int nlflags);
> + int nlflags,
> + struct netlink_ext_ack *extack);
> int (*ndo_bridge_dellink)(struct net_device *dev,
> struct nlmsghdr *nlh,
> u16 flags);
> diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
> index 95729339e7a54..a408c36a7b558 100644
> --- a/include/linux/rtnetlink.h
> +++ b/include/linux/rtnetlink.h
> @@ -222,7 +222,8 @@ extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> u32 filter_mask,
> int (*vlan_fill)(struct sk_buff *skb,
> struct net_device *dev,
> - u32 filter_mask));
> + u32 filter_mask),
> + struct netlink_ext_ack *extack);
>
> extern void rtnl_offload_xstats_notify(struct net_device *dev);
>
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index ae76df0de05a0..855a46aec3a89 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -680,7 +680,8 @@ void br_ifinfo_notify(int event, const struct net_bridge *br,
> * Dump information about all ports, in response to GETLINK
> */
> int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> - struct net_device *dev, u32 filter_mask, int nlflags)
> + struct net_device *dev, u32 filter_mask, int nlflags,
> + struct netlink_ext_ack *extack)
> {
> struct net_bridge_port *port = br_port_get_rtnl(dev);
>
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 09c397e303307..edbe7a1f57363 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -2179,7 +2179,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags,
> struct netlink_ext_ack *extack);
> int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
> int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
> - u32 filter_mask, int nlflags);
> + u32 filter_mask, int nlflags, struct netlink_ext_ack *extack);
> int br_process_vlan_info(struct net_bridge *br,
> struct net_bridge_port *p, int cmd,
> struct bridge_vlan_info *vinfo_curr,
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index e3444fd240615..a341e06f47d31 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -5420,7 +5420,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> u32 filter_mask,
> int (*vlan_fill)(struct sk_buff *skb,
> struct net_device *dev,
> - u32 filter_mask))
> + u32 filter_mask),
> + struct netlink_ext_ack *extack)
> {
> struct nlmsghdr *nlh;
> struct ifinfomsg *ifm;
> @@ -5596,7 +5597,8 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
> if (idx >= cb->args[0]) {
> err = br_dev->netdev_ops->ndo_bridge_getlink(
> skb, portid, seq, dev,
> - filter_mask, NLM_F_MULTI);
> + filter_mask, NLM_F_MULTI,
> + cb->extack);
> if (err < 0 && err != -EOPNOTSUPP) {
> if (likely(skb->len))
> break;
> @@ -5612,7 +5614,8 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
> err = ops->ndo_bridge_getlink(skb, portid,
> seq, dev,
> filter_mask,
> - NLM_F_MULTI);
> + NLM_F_MULTI,
> + cb->extack);
> if (err < 0 && err != -EOPNOTSUPP) {
> if (likely(skb->len))
> break;
> @@ -5646,7 +5649,8 @@ static inline size_t bridge_nlmsg_size(void)
> + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */
> }
>
> -static int rtnl_bridge_notify(struct net_device *dev)
> +static int rtnl_bridge_notify(struct net_device *dev,
> + struct netlink_ext_ack *extack)
> {
> struct net *net = dev_net(dev);
> struct sk_buff *skb;
> @@ -5661,7 +5665,7 @@ static int rtnl_bridge_notify(struct net_device *dev)
> goto errout;
> }
>
> - err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0, 0);
> + err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0, 0, extack);
> if (err < 0)
> goto errout;
>
> @@ -5752,7 +5756,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
> /* Generate event to notify upper layer of bridge
> * change
> */
> - err = rtnl_bridge_notify(dev);
> + err = rtnl_bridge_notify(dev, extack);
> }
> }
>
> @@ -5827,7 +5831,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
> /* Generate event to notify upper layer of bridge
> * change
> */
> - err = rtnl_bridge_notify(dev);
> + err = rtnl_bridge_notify(dev, extack);
> }
> }
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink()
2026-09-19 13:43 ` [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink() Artem Lytkin
2026-09-20 7:08 ` Nikolay Aleksandrov
@ 2026-09-22 18:44 ` netdev-bot+sashiko
1 sibling, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-09-22 18:44 UTC (permalink / raw)
To: iprintercanon
Cc: netdev, bridge, razor, idosch, davem, edumazet, kuba, pabeni,
horms, andrew+netdev, kuniyu, michael.chan, pavan.chebbi,
ajit.khaparde, sriharsha.basavapatna, anthony.l.nguyen,
przemyslaw.kitszel, intel-wired-lan, saeedm, tariqt, mbloch,
oss-drivers, wintera, aswin, linux-s390
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] Patch 1 ("rtnetlink: pass extack to ndo_bridge_getlink()")
threads a `struct netlink_ext_ack *extack` through the…
Pre-existing issues:
- [Medium] This problem wasn't introduced by this patch, but in
`br_fill_ifinfo()` (net/bridge/br_netlink.c) the VLAN-fill result is…
--
rtnetlink: pass extack to ndo_bridge_getlink()
The patch adds a struct netlink_ext_ack *extack argument to the
ndo_bridge_getlink op, to ndo_dflt_bridge_getlink() and to br_getlink(),
and updates the eight in-tree implementations plus the two rtnetlink call
sites. The commit describes itself as no functional change.
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index e3444fd24061..a341e06f47d3 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -5420,7 +5420,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> u32 filter_mask,
> int (*vlan_fill)(struct sk_buff *skb,
> struct net_device *dev,
> - u32 filter_mask))
> + u32 filter_mask),
> + struct netlink_ext_ack *extack)
> {
> struct nlmsghdr *nlh;
> struct ifinfomsg *ifm;
[Severity: Low]
Taken on its own, does this patch leave extack unused in every callee it
touches?
ndo_dflt_bridge_getlink() gains the parameter but none of its failure
paths reference it:
net/core/rtnetlink.c:ndo_dflt_bridge_getlink() {
...
nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), nlflags);
if (nlh == NULL)
return -EMSGSIZE;
...
nla_put_failure:
nlmsg_cancel(skb, nlh);
return err ? err : -EMSGSIZE;
}
So for the eight drivers that use the default helper the extack stays
empty, and the commit message statement "So the bridge can say why filling
the link info failed" is only realized by the next patch in the series,
which adds the extack argument to br_fill_ifinfo() and emits
NL_SET_ERR_MSG_MOD() there. Would it help to say in the changelog that
this is preparation and that the message is added by the following patch?
[ ... ]
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index ae76df0de05a..855a46aec3a8 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -680,7 +680,8 @@ void br_ifinfo_notify(int event, const struct net_bridge *br,
> * Dump information about all ports, in response to GETLINK
> */
> int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
> - struct net_device *dev, u32 filter_mask, int nlflags)
> + struct net_device *dev, u32 filter_mask, int nlflags,
> + struct netlink_ext_ack *extack)
> {
> struct net_bridge_port *port = br_port_get_rtnl(dev);
>
[Severity: Low]
br_getlink() accepts extack here but drops it, since the call below is
left unchanged:
net/bridge/br_netlink.c:br_getlink() {
...
return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
filter_mask, dev, true);
}
br_fill_ifinfo() is the function that actually returns -EMSGSIZE, so does
anything in the bridge get to use the new argument until the follow-up
patch wires it into br_fill_ifinfo()?
While looking at br_fill_ifinfo() for the above, one unrelated
observation:
[Severity: Medium]
This isn't a bug introduced by this patch, but in br_fill_ifinfo() the
VLAN fill result is overwritten before it is tested:
net/bridge/br_netlink.c:br_fill_ifinfo() {
...
if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
err = br_fill_ifvlaninfo_compressed(skb, vg);
else
err = br_fill_ifvlaninfo(skb, vg);
if (port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags))
err = br_fill_vlan_tunnel_info(skb, vg);
rcu_read_unlock();
if (err)
goto nla_put_failure;
...
}
Can an -EMSGSIZE from br_fill_ifvlaninfo[_compressed]() be lost here?
br_fill_vlan_tunnel_info() in net/bridge/br_netlink_tunnel.c skips every
VLAN that has no tinfo.tunnel_dst and returns 0 without touching the skb,
which happens after "bridge link set dev X vlan_tunnel on" with no
tunnel_info id mappings configured:
net/bridge/br_netlink_tunnel.c:br_fill_vlan_tunnel_info() {
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
if (!br_vlan_should_use(v))
continue;
if (!v->tinfo.tunnel_dst)
continue;
...
return 0;
}
In that configuration a partially filled VLAN list appears to be
finalized as success, and rtnl_bridge_getlink() then advances idx and
records it in cb->args[0], so the port is not retried in the next dump
round:
net/core/rtnetlink.c:rtnl_bridge_getlink() {
...
if (err < 0 && err != -EOPNOTSUPP) {
if (likely(skb->len))
break;
goto out_err;
}
}
idx++;
...
}
Would testing the VLAN fill error before calling
br_fill_vlan_tunnel_info() be the right fix? This code is identical in
the baseline and is unchanged by the series, so it is a separate patch.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260919134333.49379-1-iprintercanon%40gmail.com
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute
2026-09-19 13:43 ` [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute Artem Lytkin
2026-09-20 7:06 ` Nikolay Aleksandrov
@ 2026-09-22 18:44 ` netdev-bot+sashiko
1 sibling, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-09-22 18:44 UTC (permalink / raw)
To: iprintercanon
Cc: netdev, bridge, razor, idosch, davem, edumazet, kuba, pabeni,
horms, andrew+netdev, kuniyu, michael.chan, pavan.chebbi,
ajit.khaparde, sriharsha.basavapatna, anthony.l.nguyen,
przemyslaw.kitszel, intel-wired-lan, saeedm, tariqt, mbloch,
oss-drivers, wintera, aswin, linux-s390
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 (2) · Low: 2
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] New error path discards already-consumed CFM status.
- [Low] Missing `Fixes:` tag / stable routing information for a user-
visible uAPI bug fix.
- [Low] The single `nla_nest_too_large` label emits "AF_SPEC info too
large, use per-object dumps (e.g. RTM_GETVLAN)" for all three overflow…
Pre-existing issues:
- [Medium] This problem wasn't introduced by this patch, but it
undermines the notification behaviour the commit message describes:…
- [Medium] This problem wasn't introduced by this patch, but it sits
directly in the rewritten function: in `br_fill_ifinfo()` the return…
--
net: bridge: fail link info that does not fit in a netlink attribute
br_fill_ifinfo() now finalizes the IFLA_AF_SPEC, IFLA_BRIDGE_CFM and
IFLA_BRIDGE_MST nests with nla_nest_end_safe() and returns -E2BIG with an
extack message when a nest payload exceeds what the u16 nla_len can
describe. br_fill_ifinfo() gains an extack argument, passed through from
br_getlink() and NULL from br_info_notify().
A few questions on the change below.
> nla_len is a u16, and a port with enough VLANs, tunnels or MST entries
> makes the IFLA_AF_SPEC nest wrap, so userspace reads garbage. Same for
> the inner MST and CFM nests.
[Severity: Low]
Since the commit message describes pre-existing user-visible breakage
("userspace reads garbage") rather than a cleanup, should this carry a
Fixes: tag so it gets routed to stable?
The overflow does look reachable: __get_vlan_tinfo_size() in
br_netlink_tunnel.c charges 28 bytes per tunnel mapping
return nla_total_size(0) + nla_total_size(sizeof(u32)) +
nla_total_size(sizeof(u16)) + nla_total_size(sizeof(u16));
so roughly 4094 VLANs with non-consecutive tunnel IDs push the AF_SPEC
payload past U16_MAX, and br_info_notify() allocates an skb of exactly
br_nlmsg_size() bytes for it.
Candidate anchors would be the commit that introduced the AF_SPEC nesting
(b6c02ef54913) and the later CFM/MST reporting additions.
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 855a46aec3a8..fbd91b86d426 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
[ ... ]
> @@ -588,7 +588,8 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> goto nla_put_failure;
> }
>
> - nla_nest_end(skb, cfm_nest);
> + if (nla_nest_end_safe(skb, cfm_nest) < 0)
> + goto nla_nest_too_large;
> }
[Severity: Medium]
Can this new exit throw away CFM status that has already been consumed?
br_getlink() calls br_fill_ifinfo() with getlink = true, and
br_cfm_status_fill_info() clears the "seen" indications as it serializes
each record:
net/bridge/br_cfm_netlink.c:br_cfm_status_fill_info() {
...
if (getlink) {
/* Clear all 'seen' indications */
mep->status.opcode_unexp_seen = false;
mep->status.version_unexp_seen = false;
mep->status.rx_level_low_seen = false;
}
...
if (getlink) { /* Only clear if this is a GETLINK */
/* Clear all 'seen' indications */
peer_mep->cc_status.seen = false;
peer_mep->cc_status.tlv_seen = false;
peer_mep->cc_status.seq_unexp_seen = false;
}
...
}
Nothing bounds the peer MEP count so that the IFLA_BRIDGE_CFM nest stays
below U16_MAX (each peer status record is around 76 bytes, so roughly 860
peers overflow it). The nest check then jumps to nla_nest_too_large, which
cancels the whole message and returns -E2BIG, after the flags were already
cleared.
A retry returns -E2BIG again, so that generation of defect and
unexpected-opcode information is gone. Before this patch the malformed
message was at least delivered. The existing nla_put_failure path has the
same clear-then-cancel shape; would validating the nest size before
consuming the state be preferable to adding another cancel-after-clear
exit?
> @@ -608,20 +609,27 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> if (err)
> goto nla_put_failure;
>
> - nla_nest_end(skb, mst_nest);
> + if (nla_nest_end_safe(skb, mst_nest) < 0)
> + goto nla_nest_too_large;
> }
>
> done:
> if (af) {
> - if (nlmsg_get_pos(skb) - (void *)af > nla_attr_size(0))
> - nla_nest_end(skb, af);
> - else
> + if (nla_nest_end_safe(skb, af) < 0)
> + goto nla_nest_too_large;
> + if (!nla_len(af))
> nla_nest_cancel(skb, af);
> }
>
> nlmsg_end(skb, nlh);
> return 0;
>
[Severity: Medium]
This isn't a bug introduced by this patch, but it lands right in the
rewritten function: can the done: block finalize a truncated AF_SPEC nest
and report success?
Earlier in br_fill_ifinfo() the VLAN fill result is clobbered:
if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
err = br_fill_ifvlaninfo_compressed(skb, vg);
else
err = br_fill_ifvlaninfo(skb, vg);
if (port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags))
err = br_fill_vlan_tunnel_info(skb, vg);
rcu_read_unlock();
if (err)
goto nla_put_failure;
br_fill_vlan_tunnel_info() in br_netlink_tunnel.c returns 0 without writing
anything when no VLAN in the group has a tunnel_dst:
if (!v->tinfo.tunnel_dst)
continue;
...
return 0;
vlan_tunnel is a port flag that can be set without any per-VLAN tunnel
mapping, so an -EMSGSIZE from the VLAN fill is overwritten with 0, the
goto nla_put_failure is skipped, and control reaches done: which ends the
nest and returns 0. In a dump rtnl_bridge_getlink() then does
idx++;
and moves past the device, so the missing VLANs are never emitted.
> +nla_nest_too_large:
> + NL_SET_ERR_MSG_MOD(extack,
> + "AF_SPEC info too large, use per-object dumps (e.g. RTM_GETVLAN)");
> + nlmsg_cancel(skb, nlh);
> + return -E2BIG;
> +
[Severity: Low]
All three overflow sites share this label, so an oversized
IFLA_BRIDGE_CFM nest also tells userspace to retry with RTM_GETVLAN.
The bridge only registers PF_BRIDGE handlers for the VLAN and MDB commands:
net/bridge/br_vlan.c:
{THIS_MODULE, PF_BRIDGE, RTM_GETVLAN, NULL, br_vlan_rtm_dump, 0},
There is no RTM_GET* command that dumps CFM MEP config or status; those
attributes are only produced by br_cfm_config_fill_info() and
br_cfm_status_fill_info(), reachable solely from br_fill_ifinfo(), and
include/uapi/linux/rtnetlink.h has no CFM command. The same applies to the
per-port IFLA_BRIDGE_MST nest, for which RTM_GETVLAN returns only the
indirect per-VLAN state.
Would a per-site message be better here, so the advice matches what the
kernel can actually return?
> nla_put_failure:
> nlmsg_cancel(skb, nlh);
> return -EMSGSIZE;
> @@ -654,7 +662,8 @@ void br_info_notify(int event, const struct net_bridge *br,
> if (skb == NULL)
> goto errout;
>
> - err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false);
> + err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false,
> + NULL);
> if (err < 0) {
> /* -EMSGSIZE implies BUG in br_nlmsg_size() */
> WARN_ON(err == -EMSGSIZE);
[Severity: Medium]
This is a pre-existing issue, but it affects the notification behaviour
described in the changelog: on the CFM status notification path the skb can
be too small, so this WARN_ON fires before the new -E2BIG check is reached.
br_cfm_status_fill_info() opens one nest per record:
net/bridge/br_cfm_netlink.c:br_cfm_status_fill_info() {
...
tb = nla_nest_start(skb, IFLA_BRIDGE_CFM_MEP_STATUS_INFO);
if (!tb)
goto nla_info_failure;
...
}
and likewise one IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO nest per peer MEP, but
br_get_link_af_size_filtered() only charges the outer nest plus the child
attributes:
net/bridge/br_netlink.c:br_get_link_af_size_filtered() {
...
vinfo_sz += nla_total_size(0); /* IFLA_BRIDGE_CFM */
/* For each status struct the MEP instance (u32) is added */
vinfo_sz += num_cfm_mep_infos *
(nla_total_size(sizeof(u32)) ...
...
}
No nla_total_size(0) is added for the enclosing per-record nests, so the
estimate is 4 bytes short per record. br_cfm_notify() reaches this path:
net/bridge/br_cfm.c:br_cfm_notify() {
u32 filter = RTEXT_FILTER_CFM_STATUS;
br_info_notify(event, port->br, NULL, filter);
}
The deficit grows with the record count while the slack from kmalloc bucket
rounding does not, so for some MEP/peer counts nla_nest_start() fails with
-EMSGSIZE, the "implies BUG in br_nlmsg_size()" WARN_ON triggers and the
notification is dropped instead of taking the new -E2BIG path.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260919134333.49379-1-iprintercanon%40gmail.com
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-22 18:44 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-19 13:43 [PATCH net-next v3 0/2] bridge: report an oversized IFLA_AF_SPEC nest instead of truncating Artem Lytkin
2026-09-19 13:43 ` [PATCH net-next v3 1/2] rtnetlink: pass extack to ndo_bridge_getlink() Artem Lytkin
2026-09-20 7:08 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
2026-09-19 13:43 ` [PATCH net-next v3 2/2] net: bridge: fail link info that does not fit in a netlink attribute Artem Lytkin
2026-09-20 7:06 ` Nikolay Aleksandrov
2026-09-22 18:44 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox