From patchwork Thu Jan 10 03:32:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mauricio Faria de Oliveira X-Patchwork-Id: 1022699 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43Zs5Y2BdFz9sLw; Thu, 10 Jan 2019 14:33:05 +1100 (AEDT) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1ghR5W-0002ve-PM; Thu, 10 Jan 2019 03:32:58 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.86_2) (envelope-from ) id 1ghR5U-0002tX-KI for kernel-team@lists.ubuntu.com; Thu, 10 Jan 2019 03:32:56 +0000 Received: from mail-qt1-f198.google.com ([209.85.160.198]) by youngberry.canonical.com with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.76) (envelope-from ) id 1ghR5U-0001sg-AQ for kernel-team@lists.ubuntu.com; Thu, 10 Jan 2019 03:32:56 +0000 Received: by mail-qt1-f198.google.com with SMTP id k90so8931275qte.0 for ; Wed, 09 Jan 2019 19:32:56 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=HshYjCgYbcGy1uArpvmvdUpy8IgMMQ7RF94dG44S8i8=; b=gENS6e8PWfyLLzipUWNuTFCZF4MtoVEDVkVd/QsmTIRol1QefBRDVjf23iVttt3nHl k/uzXChcedij7j4P7NZRKYh7avnXBgZ8JhPTuoH4Xdlr5GAETwHY5gSP0BLjyznAQmVA 5vSqlg+hOEHLcNqle9bEilhI7ts1OyV6YnK383jKOu+6y6yvlkjOK6qi5PCsq4jJcALx knqiuqOd7IVDq1HyauiaOq04gQbLkGkjhFy+JO3tOOTKB+h6eF30u7mnDFyji2gcHl1K vDBMXGRoDT8CzTozpcIjxh2BMpvdSqwZQyEVJ145VpB7GBLMm4VlyvHPPT+jiaia4j1B v+lw== X-Gm-Message-State: AJcUukcOFPrb81eeQPJ0ypL9W3/3v63ZGIl7mm6sRQXtSYINfMxtz+OQ 0VJhKe8O0VGr3jAhz1gaMpmwgrtqjvwvQJKaWGOPlvSGTIyrlp0NVw2v5xFmQXegjLh4xZvr27g kBMfZvqAYsoGYnQe09KdMXQTk6jc+KtCo1K47qdkqbg== X-Received: by 2002:a05:620a:9:: with SMTP id j9mr7819124qki.144.1547091175396; Wed, 09 Jan 2019 19:32:55 -0800 (PST) X-Google-Smtp-Source: ALg8bN4P/FzYfrHQDHvCo2bnZbttXcTsOLOglnpppn29E8XYFng7CZCT3Ht/7AG6JDtn6Ai8tRsUew== X-Received: by 2002:a05:620a:9:: with SMTP id j9mr7819112qki.144.1547091175183; Wed, 09 Jan 2019 19:32:55 -0800 (PST) Received: from localhost.localdomain ([177.181.227.0]) by smtp.gmail.com with ESMTPSA id 186sm15213337qke.10.2019.01.09.19.32.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 Jan 2019 19:32:54 -0800 (PST) From: Mauricio Faria de Oliveira To: kernel-team@lists.ubuntu.com Subject: [SRU X][PATCH 5/6] netfilter: nf_conncount: fix garbage collection confirm race Date: Thu, 10 Jan 2019 01:32:03 -0200 Message-Id: <20190110033204.31413-6-mfo@canonical.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190110033204.31413-1-mfo@canonical.com> References: <20190110033204.31413-1-mfo@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Florian Westphal BugLink: https://bugs.launchpad.net/bugs/1811094 Yi-Hung Wei and Justin Pettit found a race in the garbage collection scheme used by nf_conncount. When doing list walk, we lookup the tuple in the conntrack table. If the lookup fails we remove this tuple from our list because the conntrack entry is gone. This is the common cause, but turns out its not the only one. The list entry could have been created just before by another cpu, i.e. the conntrack entry might not yet have been inserted into the global hash. The avoid this, we introduce a timestamp and the owning cpu. If the entry appears to be stale, evict only if: 1. The current cpu is the one that added the entry, or, 2. The timestamp is older than two jiffies The second constraint allows GC to be taken over by other cpu too (e.g. because a cpu was offlined or napi got moved to another cpu). We can't pretend the 'doubtful' entry wasn't in our list. Instead, when we don't find an entry indicate via IS_ERR that entry was removed ('did not exist' or withheld ('might-be-unconfirmed'). This most likely also fixes a xt_connlimit imbalance earlier reported by Dmitry Andrianov. Cc: Dmitry Andrianov Reported-by: Justin Pettit Reported-by: Yi-Hung Wei Signed-off-by: Florian Westphal Acked-by: Yi-Hung Wei Signed-off-by: Pablo Neira Ayuso (backported from commit b36e4523d4d56e2595e28f16f6ccf1cd6a9fc452) [mfo: backport: refresh context lines and use older symbol/file names: - nf_conncount.c -> xt_connlimit.c. - nf_conncount_rb -> xt_connlimit_rb - nf_conncount_tuple -> xt_connlimit_conn - conncount_conn_cachep -> connlimit_conn_cachep] Signed-off-by: Mauricio Faria de Oliveira --- net/netfilter/xt_connlimit.c | 52 ++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index eb353c0b33a8..b64186de28e5 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -47,6 +47,8 @@ struct xt_connlimit_conn { struct hlist_node node; struct nf_conntrack_tuple tuple; struct nf_conntrack_zone zone; + int cpu; + u32 jiffies32; }; struct xt_connlimit_rb { @@ -127,11 +129,42 @@ bool nf_conncount_add(struct hlist_head *head, return false; conn->tuple = *tuple; conn->zone = *zone; + conn->cpu = raw_smp_processor_id(); + conn->jiffies32 = (u32)jiffies; hlist_add_head(&conn->node, head); return true; } EXPORT_SYMBOL_GPL(nf_conncount_add); +static const struct nf_conntrack_tuple_hash * +find_or_evict(struct net *net, struct xt_connlimit_conn *conn) +{ + const struct nf_conntrack_tuple_hash *found; + unsigned long a, b; + int cpu = raw_smp_processor_id(); + __s32 age; + + found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple); + if (found) + return found; + b = conn->jiffies32; + a = (u32)jiffies; + + /* conn might have been added just before by another cpu and + * might still be unconfirmed. In this case, nf_conntrack_find() + * returns no result. Thus only evict if this cpu added the + * stale entry or if the entry is older than two jiffies. + */ + age = a - b; + if (conn->cpu == cpu || age >= 2) { + hlist_del(&conn->node); + kmem_cache_free(connlimit_conn_cachep, conn); + return ERR_PTR(-ENOENT); + } + + return ERR_PTR(-EAGAIN); +} + unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_zone *zone, @@ -139,8 +172,8 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head, { const struct nf_conntrack_tuple_hash *found; struct xt_connlimit_conn *conn; - struct hlist_node *n; struct nf_conn *found_ct; + struct hlist_node *n; unsigned int length = 0; *addit = true; @@ -148,10 +181,19 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head, /* check the saved connections */ hlist_for_each_entry_safe(conn, n, head, node) { - found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple); - if (found == NULL) { - hlist_del(&conn->node); - kmem_cache_free(connlimit_conn_cachep, conn); + found = find_or_evict(net, conn); + if (IS_ERR(found)) { + /* Not found, but might be about to be confirmed */ + if (PTR_ERR(found) == -EAGAIN) { + length++; + if (!tuple) + continue; + + if (nf_ct_tuple_equal(&conn->tuple, tuple) && + nf_ct_zone_id(&conn->zone, conn->zone.dir) == + nf_ct_zone_id(zone, zone->dir)) + *addit = false; + } continue; }