diff --git a/net/sched/act_api.c b/net/sched/act_api.c index d9d4f7097624..6b751e274ce0 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -555,7 +555,8 @@ tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) goto nla_put_failure; err = tcf_action_dump_old(skb, a, bind, ref); if (err > 0) { - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; return err; } @@ -1276,7 +1277,9 @@ int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], tcf_action_dump_1(skb, a, bind, ref); if (err < 0) goto errout; - nla_nest_end(skb, nest); + err = nla_nest_end_safe(skb, nest); + if (err < 0) + goto errout; } return 0; @@ -1674,27 +1677,42 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], struct nlmsghdr *nlh; unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; + int err; nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags); - if (!nlh) + if (!nlh) { + err = -EMSGSIZE; goto out_nlmsg_trim; + } t = nlmsg_data(nlh); t->tca_family = AF_UNSPEC; t->tca__pad1 = 0; t->tca__pad2 = 0; if (extack && extack->_msg && - nla_put_string(skb, TCA_ROOT_EXT_WARN_MSG, extack->_msg)) + nla_put_string(skb, TCA_ROOT_EXT_WARN_MSG, extack->_msg)) { + err = -EMSGSIZE; goto out_nlmsg_trim; + } nest = nla_nest_start_noflag(skb, TCA_ACT_TAB); - if (!nest) + if (!nest) { + err = -EMSGSIZE; goto out_nlmsg_trim; + } - if (tcf_action_dump(skb, actions, bind, ref, false) < 0) + err = tcf_action_dump(skb, actions, bind, ref, false); + if (err < 0) { + /* Report any inner dump failure as -EMSGSIZE so + * tcf_del_notify() drops the notification, not the delete. + */ + err = -EMSGSIZE; goto out_nlmsg_trim; + } - nla_nest_end(skb, nest); + err = nla_nest_end_safe(skb, nest); + if (err < 0) + goto out_nlmsg_trim; nlh->nlmsg_len = skb_tail_pointer(skb) - b; @@ -1702,7 +1720,7 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], out_nlmsg_trim: nlmsg_trim(skb, b); - return -1; + return err; } static int @@ -1989,17 +2007,19 @@ static struct sk_buff *tcf_del_notify_msg(struct net *net, struct nlmsghdr *n, struct netlink_ext_ack *extack) { struct sk_buff *skb; + int err; skb = alloc_skb(max(attr_size + tcf_action_warn_attr_size(extack), NLMSG_GOODSIZE), GFP_KERNEL); if (!skb) return ERR_PTR(-ENOBUFS); - if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, - 0, 2, extack) <= 0) { + err = tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, + 0, 2, extack); + if (err <= 0) { NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes"); kfree_skb(skb); - return ERR_PTR(-EINVAL); + return ERR_PTR(err ? err : -EINVAL); } return skb; @@ -2017,8 +2037,14 @@ static int tcf_del_notify(struct net *net, struct nlmsghdr *n, } else { skb = tcf_del_notify_msg(net, n, actions, portid, attr_size, extack); - if (IS_ERR(skb)) - return PTR_ERR(skb); + if (IS_ERR(skb)) { + /* Such a dump can never be serialized: drop the + * notification rather than refuse the delete. + */ + if (PTR_ERR(skb) != -EMSGSIZE) + return PTR_ERR(skb); + skb = NULL; + } } /* now do the delete */ diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index 6d6d45e03c07..14801c604bd9 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -654,7 +654,8 @@ static int tcf_gate_dump(struct sk_buff *skb, struct tc_action *a, goto nla_put_failure; } - nla_nest_end(skb, entry_list); + if (nla_nest_end_safe(skb, entry_list) < 0) + goto nla_put_failure; tcf_tm_dump(&t, &gact->tcf_tm); if (nla_put_64bit(skb, TCA_GATE_TM, sizeof(t), &t, TCA_GATE_PAD)) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index a9f54988561f..bd0d635fe3fe 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -2149,11 +2149,16 @@ static struct sk_buff *tfilter_notify_prep(struct net *net, rtnl_held, extack); if (ret <= 0) { kfree_skb(skb); - if (ret == -EMSGSIZE) { - size += NLMSG_GOODSIZE; - goto retry; - } - return ERR_PTR(-EINVAL); + if (ret != -EMSGSIZE) + return ERR_PTR(-EINVAL); + /* A u16 nest caps the dump, so once the skb is larger than + * any valid message it can never serialize. Stop at the + * bound instead of looping until alloc_skb() fails. + */ + if (size > U16_MAX + NLMSG_GOODSIZE) + return ERR_PTR(-EMSGSIZE); + size += NLMSG_GOODSIZE; + goto retry; } return skb; } @@ -2200,8 +2205,18 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, skb = tfilter_notify_prep(net, oskb, n, tp, block, q, parent, fh, RTM_DELTFILTER, portid, rtnl_held, extack); if (IS_ERR(skb)) { - NL_SET_ERR_MSG(extack, "Failed to build del event notification"); - return PTR_ERR(skb); + if (PTR_ERR(skb) != -EMSGSIZE) { + NL_SET_ERR_MSG(extack, "Failed to build del event notification"); + return PTR_ERR(skb); + } + /* No notification can ever be built for this filter: drop + * it rather than refuse the delete. + */ + err = tp->ops->delete(tp, fh, last, rtnl_held, extack); + if (!err) + NL_SET_ERR_MSG(extack, + "Filter deleted; del event notification could not be built"); + return err; } err = tp->ops->delete(tp, fh, last, rtnl_held, extack); @@ -3529,7 +3544,8 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) if (tcf_action_dump(skb, exts->actions, 0, 0, false) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; } else if (exts->police) { struct tc_action *act = tcf_exts_first_act(exts); nest = nla_nest_start_noflag(skb, exts->police); @@ -3537,7 +3553,8 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) goto nla_put_failure; if (tcf_action_dump_old(skb, act, 0, 0) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; } } return 0; @@ -3565,7 +3582,8 @@ int tcf_exts_terse_dump(struct sk_buff *skb, struct tcf_exts *exts) if (tcf_action_dump(skb, exts->actions, 0, 0, true) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index e2a94ba9fba7..ba859110faae 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -305,7 +305,8 @@ static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 188cf0f949dd..232796d3a51f 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -631,7 +631,8 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, void *fh, nla_put_u32(skb, TCA_BPF_FLAGS_GEN, prog->gen_flags)) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &prog->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 210fd9fd26d8..28701d3916b8 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -185,7 +185,8 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, void *fh, tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &head->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index a9ac3acf6eda..2ef449706638 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -681,7 +681,8 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, void *fh, tcf_em_tree_dump(skb, &f->ematches, TCA_FLOW_EMATCHES) < 0) goto nla_put_failure; #endif - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 0c4beff18d68..299b1493d74b 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -3759,7 +3759,8 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, if (tcf_exts_dump(skb, &f->exts)) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; @@ -3804,7 +3805,8 @@ static int fl_terse_dump(struct net *net, struct tcf_proto *tp, void *fh, if (tcf_exts_terse_dump(skb, &f->exts)) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; return skb->len; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index a462b262719c..0f731595f839 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -414,7 +414,8 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh, if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index c14899b935bf..6ece63b82775 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -366,7 +366,8 @@ static int mall_dump(struct net *net, struct tcf_proto *tp, void *fh, if (tcf_exts_dump(skb, &head->exts)) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &head->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index dca812a75269..6f8b4a98bb84 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -663,7 +663,8 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh, if (tcf_exts_dump(skb, &f->exts) < 0) goto nla_put_failure; - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (tcf_exts_dump_stats(skb, &f->exts) < 0) goto nla_put_failure; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 76ce2d124079..91f4e6458785 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1482,7 +1482,8 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh, #endif } - nla_nest_end(skb, nest); + if (nla_nest_end_safe(skb, nest) < 0) + goto nla_put_failure; if (TC_U32_KEY(n->handle)) if (tcf_exts_dump_stats(skb, &n->exts) < 0)