From mboxrd@z Thu Jan 1 00:00:00 1970 From: David Miller Subject: Re: [PATCH net] ipv4: Fix use-after-free when flushing FIB tables Date: Tue, 19 Dec 2017 11:32:21 -0500 (EST) Message-ID: <20171219.113221.332118764426444996.davem@davemloft.net> References: <20171218081320.29442-1-idosch@mellanox.com> Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Cc: netdev@vger.kernel.org, alexander.h.duyck@intel.com, fengguang.wu@intel.com, dsahern@gmail.com, mlxsw@mellanox.com To: idosch@mellanox.com Return-path: Received: from shards.monkeyblade.net ([184.105.139.130]:52926 "EHLO shards.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752052AbdLSQcX (ORCPT ); Tue, 19 Dec 2017 11:32:23 -0500 In-Reply-To: <20171218081320.29442-1-idosch@mellanox.com> Sender: netdev-owner@vger.kernel.org List-ID: From: Ido Schimmel Date: Mon, 18 Dec 2017 10:13:20 +0200 > Since commit 0ddcf43d5d4a ("ipv4: FIB Local/MAIN table collapse") the > local table uses the same trie allocated for the main table when custom > rules are not in use. > > When a net namespace is dismantled, the main table is flushed and freed > (via an RCU callback) before the local table. In case the callback is > invoked before the local table is iterated, a use-after-free can occur. > > Fix this by iterating over the FIB tables in reverse order, so that the > main table is always freed after the local table. > > Fixes: 0ddcf43d5d4a ("ipv4: FIB Local/MAIN table collapse") > Signed-off-by: Ido Schimmel > Reported-by: Fengguang Wu This is really too clever of a fix I think :-) I would prefer if we fixed things more explicitly. In struct fib_table you can add a "data_ref" integer. Any pointer reference created to fib_table->__data increases this counter. It is always done inside of RTNL locking, so should be doable without atomics or extra locking. For a non-aliased fib_table we go: if (!--fib_table->data_ref) kfree(fib_table); And for aliased ones we do something like: if (fib_table->tb_data != fib_table->__data) { void *data = fib_table->fb_data; struct fib_table *alias; alias = container_of(data, struct fib_table, __data[0]); if (!--alias->data_ref) kfree(alias); kfree(fib_table); } Something like that.