Skip to content

Commit 4cfc0b4

Browse files
zx2c4gregkh
authored andcommitted
af_netlink: ensure that NLMSG_DONE never fails in dumps
[ Upstream commit 0642840b8bb008528dbdf929cec9f65ac4231ad0 ] The way people generally use netlink_dump is that they fill in the skb as much as possible, breaking when nla_put returns an error. Then, they get called again and start filling out the next skb, and again, and so forth. The mechanism at work here is the ability for the iterative dumping function to detect when the skb is filled up and not fill it past the brim, waiting for a fresh skb for the rest of the data. However, if the attributes are small and nicely packed, it is possible that a dump callback function successfully fills in attributes until the skb is of size 4080 (libmnl's default page-sized receive buffer size). The dump function completes, satisfied, and then, if it happens to be that this is actually the last skb, and no further ones are to be sent, then netlink_dump will add on the NLMSG_DONE part: nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); It is very important that netlink_dump does this, of course. However, in this example, that call to nlmsg_put_answer will fail, because the previous filling by the dump function did not leave it enough room. And how could it possibly have done so? All of the nla_put variety of functions simply check to see if the skb has enough tailroom, independent of the context it is in. In order to keep the important assumptions of all netlink dump users, it is therefore important to give them an skb that has this end part of the tail already reserved, so that the call to nlmsg_put_answer does not fail. Otherwise, library authors are forced to find some bizarre sized receive buffer that has a large modulo relative to the common sizes of messages received, which is ugly and buggy. This patch thus saves the NLMSG_DONE for an additional message, for the case that things are dangerously close to the brim. This requires keeping track of the errno from ->dump() across calls. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent ef206ea commit 4cfc0b4

2 files changed

Lines changed: 12 additions & 6 deletions

File tree

net/netlink/af_netlink.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,7 +2077,7 @@ static int netlink_dump(struct sock *sk)
20772077
struct sk_buff *skb = NULL;
20782078
struct nlmsghdr *nlh;
20792079
struct module *module;
2080-
int len, err = -ENOBUFS;
2080+
int err = -ENOBUFS;
20812081
int alloc_min_size;
20822082
int alloc_size;
20832083

@@ -2125,9 +2125,11 @@ static int netlink_dump(struct sock *sk)
21252125
skb_reserve(skb, skb_tailroom(skb) - alloc_size);
21262126
netlink_skb_set_owner_r(skb, sk);
21272127

2128-
len = cb->dump(skb, cb);
2128+
if (nlk->dump_done_errno > 0)
2129+
nlk->dump_done_errno = cb->dump(skb, cb);
21292130

2130-
if (len > 0) {
2131+
if (nlk->dump_done_errno > 0 ||
2132+
skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) {
21312133
mutex_unlock(nlk->cb_mutex);
21322134

21332135
if (sk_filter(sk, skb))
@@ -2137,13 +2139,15 @@ static int netlink_dump(struct sock *sk)
21372139
return 0;
21382140
}
21392141

2140-
nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI);
2141-
if (!nlh)
2142+
nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE,
2143+
sizeof(nlk->dump_done_errno), NLM_F_MULTI);
2144+
if (WARN_ON(!nlh))
21422145
goto errout_skb;
21432146

21442147
nl_dump_check_consistent(cb, nlh);
21452148

2146-
memcpy(nlmsg_data(nlh), &len, sizeof(len));
2149+
memcpy(nlmsg_data(nlh), &nlk->dump_done_errno,
2150+
sizeof(nlk->dump_done_errno));
21472151

21482152
if (sk_filter(sk, skb))
21492153
kfree_skb(skb);
@@ -2208,6 +2212,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
22082212
cb->skb = skb;
22092213

22102214
nlk->cb_running = true;
2215+
nlk->dump_done_errno = INT_MAX;
22112216

22122217
mutex_unlock(nlk->cb_mutex);
22132218

net/netlink/af_netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct netlink_sock {
3838
wait_queue_head_t wait;
3939
bool bound;
4040
bool cb_running;
41+
int dump_done_errno;
4142
struct netlink_callback cb;
4243
struct mutex *cb_mutex;
4344
struct mutex cb_def_mutex;

0 commit comments

Comments
 (0)