[PATCH] netfilter: flowtable: flush delete work after final GC

Chengfeng Ye <[email protected]>
Newsgroups gmane.linux.kernel.stable,gmane.comp.security.firewalls.netfilter.devel,gmane.linux.network,gmane.linux.kernel
Message-ID <[email protected]>
nf_flow_table_offload_flush_cleanup() drains delete work before its final
garbage-collection pass. That pass can itself queue more delete work.

During teardown, one possible interleaving is:

  teardown worker                         delete worker
  first GC: work allocation fails
    leave flow retryable
  drain delete workqueue
  final GC: allocation succeeds
    queue FLOW_CLS_DESTROY work
  destroy rhashtable
  free flowtable
                                          read flowtable->net

nf_flow_table_offload_flush_cleanup() flushes only the delete work that
was queued before the final GC. NF_FLOW_HW_DYING is set only after
successful work allocation, so a GFP_ATOMIC allocation failure during the
first GC leaves the entry eligible for retry. If that retry later succeeds
in the final GC, it queues new destroy work after the earlier flush, and
that work can retain a stale flowtable pointer past the free.

This was reproducible with failslab forcing the initial GFP_ATOMIC
allocation failure, and KASAN reported:

  BUG: KASAN: slab-use-after-free in flow_offload_work_handler+0xbe8/0xe30
  Read of size 8 at addr ffff888109c9fd98 by task kworker/u16:3/397
  Workqueue: nf_ft_offload_del flow_offload_work_handler
  Call Trace:
   print_report+0xd0/0x630
   kasan_report+0xce/0x100
   flow_offload_work_handler+0xbe8/0xe30
   process_one_work+0x63a/0x1070
   worker_thread+0x45b/0xd10
   kthread+0x2c6/0x3b0
   ret_from_fork+0x36e/0x5a0
  Allocated by task 87:
   nf_tables_newflowtable+0x5d0/0x22f0
   nfnetlink_rcv_batch+0x1396/0x1d00
   netlink_unicast+0x5f5/0x860
   netlink_sendmsg+0x70a/0xba0
  Freed by task 11:
   kfree+0x131/0x3c0
   nf_tables_trans_destroy_work+0xb26/0xeb0
   process_one_work+0x63a/0x1070
   worker_thread+0x45b/0xd10
  Last potentially related work creation:
   __queue_work+0x68e/0x1030
   flow_offload_del+0x74c/0xad0
   nf_flow_offload_gc_step+0x264/0x8e0
   nf_flow_table_gc_run+0xcd/0x150
   nf_flow_table_offload_flush_cleanup+0x5c/0x70
   nf_flow_table_free+0x280/0x350
   nf_tables_flowtable_destroy+0x71/0x270

Flush the delete workqueue again after the final GC. This keeps every
successfully queued destroy operation within the flowtable lifetime.

Fixes: c921ffe85333 ("netfilter: flowtable: Fix flushing of offloaded flows on free")
Cc: [email protected]
Signed-off-by: Chengfeng Ye <[email protected]>
---
 net/netfilter/nf_flow_table_offload.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 801a3dd9ceea..0fd09554b9ce 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -1167,6 +1167,7 @@ void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable)
 	if (nf_flowtable_hw_offload(flowtable)) {
 		flush_workqueue(nf_flow_offload_del_wq);
 		nf_flow_table_gc_run(flowtable);
+		flush_workqueue(nf_flow_offload_del_wq);
 	}
 }
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.