From patchwork Thu Nov 9 01:42:18 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Jason A. Donenfeld" X-Patchwork-Id: 836142 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=zx2c4.com header.i=@zx2c4.com header.b="1bNcqOnf"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yXQsZ0WDdz9t2M for ; Thu, 9 Nov 2017 12:42:58 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752911AbdKIBmq (ORCPT ); Wed, 8 Nov 2017 20:42:46 -0500 Received: from frisell.zx2c4.com ([192.95.5.64]:51009 "EHLO frisell.zx2c4.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751566AbdKIBmo (ORCPT ); Wed, 8 Nov 2017 20:42:44 -0500 Received: by frisell.zx2c4.com (ZX2C4 Mail Server) with ESMTP id f6d00325; Thu, 9 Nov 2017 01:39:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=zx2c4.com; h=from:to:cc :subject:date:message-id:in-reply-to:references; s=mail; bh=rmb9 +r26pWvWwv/MVZXoroa6TQA=; b=1bNcqOnf2IpS/RW6EXTAujtqylGXnvfO9mwt 9JXp6VL5SSwabKiUQnoFdOWu6rmiWuRm5W5WB4akctiuOgt3T9meQVu35kcZiTKT wqKAOuosHg1mvUvWXzzCZTKtXe0/NlKuEv2Y+Ade39OK+52hhU97KHUvPBliNgII 7rW7qh0nyYqY1KrLTbyo/7WT1fRKh0M8pBt31IP8rcqVc6whHYy2XwGkdnnxpQ7O he7jVKyawjqs8ZnAls06UHb9DHem+2JK7gzEgZrn/bUulyI2iLsIjwzsB44yZwbc RdC+WxKL3LVVUv7K8YlP49M78wYAAYKINkkhm+CGwnun1pjC7Q== Received: by frisell.zx2c4.com (ZX2C4 Mail Server) with ESMTPSA id 40a971bf (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256:NO); Thu, 9 Nov 2017 01:39:18 +0000 (UTC) From: "Jason A. Donenfeld" To: Johannes Berg , davem@davemloft.net, Netdev , linux-kernel@vger.kernel.org Cc: "Jason A. Donenfeld" Subject: [PATCH v3] af_netlink: ensure that NLMSG_DONE never fails in dumps Date: Thu, 9 Nov 2017 10:42:18 +0900 Message-Id: <20171109014218.20562-1-Jason@zx2c4.com> In-Reply-To: <20171108072141.1786-1-Jason@zx2c4.com> References: <20171108072141.1786-1-Jason@zx2c4.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- Can we get this into 4.14? Is there still time? It should also be queued up for stable. Changes v2->v3: - Johannes didn't like the subject line of the patch, so the only thing that's changed in this version is the new subject line. net/netlink/af_netlink.c | 14 ++++++++------ net/netlink/af_netlink.h | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b93148e8e9fb..7020689e643e 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2136,7 +2136,7 @@ static int netlink_dump(struct sock *sk) struct sk_buff *skb = NULL; struct nlmsghdr *nlh; struct module *module; - int len, err = -ENOBUFS; + int err = -ENOBUFS; int alloc_min_size; int alloc_size; @@ -2183,9 +2183,10 @@ static int netlink_dump(struct sock *sk) skb_reserve(skb, skb_tailroom(skb) - alloc_size); netlink_skb_set_owner_r(skb, sk); - len = cb->dump(skb, cb); + if (nlk->dump_done_errno > 0) + nlk->dump_done_errno = cb->dump(skb, cb); - if (len > 0) { + if (nlk->dump_done_errno > 0 || skb_tailroom(skb) < nlmsg_total_size(sizeof(nlk->dump_done_errno))) { mutex_unlock(nlk->cb_mutex); if (sk_filter(sk, skb)) @@ -2195,13 +2196,13 @@ static int netlink_dump(struct sock *sk) return 0; } - nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); - if (!nlh) + nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(nlk->dump_done_errno), NLM_F_MULTI); + if (WARN_ON(!nlh)) goto errout_skb; nl_dump_check_consistent(cb, nlh); - memcpy(nlmsg_data(nlh), &len, sizeof(len)); + memcpy(nlmsg_data(nlh), &nlk->dump_done_errno, sizeof(nlk->dump_done_errno)); if (sk_filter(sk, skb)) kfree_skb(skb); @@ -2273,6 +2274,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb, } nlk->cb_running = true; + nlk->dump_done_errno = INT_MAX; mutex_unlock(nlk->cb_mutex); diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index 028188597eaa..962de7b3c023 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -34,6 +34,7 @@ struct netlink_sock { wait_queue_head_t wait; bool bound; bool cb_running; + int dump_done_errno; struct netlink_callback cb; struct mutex *cb_mutex; struct mutex cb_def_mutex;