]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: Improve handling of failures on link and route dumps
authorDavid Ahern <dsahern@gmail.com>
Tue, 16 May 2017 06:19:17 +0000 (23:19 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Jun 2017 10:10:02 +0000 (12:10 +0200)
[ Upstream commit f6c5775ff0bfa62b072face6bf1d40f659f194b2 ]

In general, rtnetlink dumps do not anticipate failure to dump a single
object (e.g., link or route) on a single pass. As both route and link
objects have grown via more attributes, that is no longer a given.

netlink dumps can handle a failure if the dump function returns an
error; specifically, netlink_dump adds the return code to the response
if it is <= 0 so userspace is notified of the failure. The missing
piece is the rtnetlink dump functions returning the error.

Fix route and link dump functions to return the errors if no object is
added to an skb (detected by skb->len != 0). IPv6 route dumps
(rt6_dump_route) already return the error; this patch updates IPv4 and
link dumps. Other dump functions may need to be ajusted as well.

Reported-by: Jan Moskyto Matejka <mq@ucw.cz>
Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/core/rtnetlink.c
net/ipv4/fib_frontend.c
net/ipv4/fib_trie.c

index 69daf393cbe13a29a0c4ca541559f61c8f62c11c..8d4a185a814338e8782ec77d32a9a72584f42afd 100644 (file)
@@ -1620,13 +1620,13 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
                                               cb->nlh->nlmsg_seq, 0,
                                               flags,
                                               ext_filter_mask);
-                       /* If we ran out of room on the first message,
-                        * we're in trouble
-                        */
-                       WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
 
-                       if (err < 0)
-                               goto out;
+                       if (err < 0) {
+                               if (likely(skb->len))
+                                       goto out;
+
+                               goto out_err;
+                       }
 
                        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
 cont:
@@ -1634,10 +1634,12 @@ cont:
                }
        }
 out:
+       err = skb->len;
+out_err:
        cb->args[1] = idx;
        cb->args[0] = h;
 
-       return skb->len;
+       return err;
 }
 
 int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len)
@@ -3427,8 +3429,12 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
                                err = br_dev->netdev_ops->ndo_bridge_getlink(
                                                skb, portid, seq, dev,
                                                filter_mask, NLM_F_MULTI);
-                               if (err < 0 && err != -EOPNOTSUPP)
-                                       break;
+                               if (err < 0 && err != -EOPNOTSUPP) {
+                                       if (likely(skb->len))
+                                               break;
+
+                                       goto out_err;
+                               }
                        }
                        idx++;
                }
@@ -3439,16 +3445,22 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
                                                              seq, dev,
                                                              filter_mask,
                                                              NLM_F_MULTI);
-                               if (err < 0 && err != -EOPNOTSUPP)
-                                       break;
+                               if (err < 0 && err != -EOPNOTSUPP) {
+                                       if (likely(skb->len))
+                                               break;
+
+                                       goto out_err;
+                               }
                        }
                        idx++;
                }
        }
+       err = skb->len;
+out_err:
        rcu_read_unlock();
        cb->args[0] = idx;
 
-       return skb->len;
+       return err;
 }
 
 static inline size_t bridge_nlmsg_size(void)
index 8f2133ffc2ff1b94871408a5f934cb938d3462b5..a2f7de26670e2d1664c289cfd2cdecd68f8a996d 100644 (file)
@@ -760,7 +760,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
        unsigned int e = 0, s_e;
        struct fib_table *tb;
        struct hlist_head *head;
-       int dumped = 0;
+       int dumped = 0, err;
 
        if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
            ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
@@ -780,20 +780,27 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
                        if (dumped)
                                memset(&cb->args[2], 0, sizeof(cb->args) -
                                                 2 * sizeof(cb->args[0]));
-                       if (fib_table_dump(tb, skb, cb) < 0)
-                               goto out;
+                       err = fib_table_dump(tb, skb, cb);
+                       if (err < 0) {
+                               if (likely(skb->len))
+                                       goto out;
+
+                               goto out_err;
+                       }
                        dumped = 1;
 next:
                        e++;
                }
        }
 out:
+       err = skb->len;
+out_err:
        rcu_read_unlock();
 
        cb->args[1] = e;
        cb->args[0] = h;
 
-       return skb->len;
+       return err;
 }
 
 /* Prepare and feed intra-kernel routing request.
index 2f0d8233950faeac91f287644bc9476f19f74578..08709fb62d2eb0086e04d98fcfae45b01b973927 100644 (file)
@@ -2079,6 +2079,8 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
 
        /* rcu_read_lock is hold by caller */
        hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
+               int err;
+
                if (i < s_i) {
                        i++;
                        continue;
@@ -2089,17 +2091,14 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
                        continue;
                }
 
-               if (fib_dump_info(skb, NETLINK_CB(cb->skb).portid,
-                                 cb->nlh->nlmsg_seq,
-                                 RTM_NEWROUTE,
-                                 tb->tb_id,
-                                 fa->fa_type,
-                                 xkey,
-                                 KEYLENGTH - fa->fa_slen,
-                                 fa->fa_tos,
-                                 fa->fa_info, NLM_F_MULTI) < 0) {
+               err = fib_dump_info(skb, NETLINK_CB(cb->skb).portid,
+                                   cb->nlh->nlmsg_seq, RTM_NEWROUTE,
+                                   tb->tb_id, fa->fa_type,
+                                   xkey, KEYLENGTH - fa->fa_slen,
+                                   fa->fa_tos, fa->fa_info, NLM_F_MULTI);
+               if (err < 0) {
                        cb->args[4] = i;
-                       return -1;
+                       return err;
                }
                i++;
        }
@@ -2121,10 +2120,13 @@ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb,
        t_key key = cb->args[3];
 
        while ((l = leaf_walk_rcu(&tp, key)) != NULL) {
-               if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) {
+               int err;
+
+               err = fn_trie_dump_leaf(l, tb, skb, cb);
+               if (err < 0) {
                        cb->args[3] = key;
                        cb->args[2] = count;
-                       return -1;
+                       return err;
                }
 
                ++count;