Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp5670pxb; Tue, 17 Nov 2020 18:22:35 -0800 (PST) X-Google-Smtp-Source: ABdhPJwumz8u/8nBqq+NsCz3YhKjLwL22heRZ1hjTmT5XpN4mte7PSlsqlj8nhmRmH1ATmkVGqXg X-Received: by 2002:a50:daca:: with SMTP id s10mr15281368edj.263.1605666155422; Tue, 17 Nov 2020 18:22:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605666155; cv=none; d=google.com; s=arc-20160816; b=OFhB5TdxBsqpLVwIIlwjN2B3MiFjwb4/l6Lk+fD9DlPWvZLPxOyhOfO31wmXlYuSTL bxMDzslYJVizSdmieX7y/+MN7coQqW0kymwN6fLhJ6Xq3DYItsFEV1cFTG5ofP/LR5oo 6Gd9CrDFG2wvCMsPvhnGwFqtoi8+CZkSHBd1FnDQXvoY7skugfbAnjG0tGvh5k4VlVqS Ur9YTaJ/ZDTkynOfG/j1tnSoIMaTahPZud9UkzHcrJz77Hefh/s+Zab88GFiLLv2qfth ZSvys7oKMJ9rvDs9iSNqGGQbw+f3mLIVZNRfpc17DBIFIPKPZxJY1p5NyK1OE16ErEpm ePGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:subject:cc:to:from:date; bh=3dDtJjPP4aa3qKvrE/g2xrTQbq5wN31QJxoQY2XysKc=; b=D7jhmOBjR4XiZA/H4l/DbGlfCxHvOPNKGGbCL6GQoy1w1GW5Ilp7rgc3IciEA9KmV2 VTRTHg29NkfgmX5PRgbunk30L1tktHrgMfMXESHZAMep/qWjQZhAaaNrJH3g1lYvqBGK 9vWteL92JLMIfzpnAzDHH7SEHEVVZrYfXw6o+ZwXfZ1UMSpmlu7sMvsebLyAaCqq224w wLjhW3fQCM5kdV93fjtQprAYkMLpBJHTTDgPZodN3D74sW5EbwFsuBx2eJgUvCHcfHJC T1ieIvdn7O6OSZ9biChfJX/BArARmSTW3mx4lz3WkQ+TohSRUKribv205TtVASe219+6 cbiA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id e4si14734336eds.412.2020.11.17.18.21.54; Tue, 17 Nov 2020 18:22:35 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726363AbgKRCSm (ORCPT + 99 others); Tue, 17 Nov 2020 21:18:42 -0500 Received: from mail.kernel.org ([198.145.29.99]:46136 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725771AbgKRCSm (ORCPT ); Tue, 17 Nov 2020 21:18:42 -0500 Received: from oasis.local.home (cpe-66-24-58-225.stny.res.rr.com [66.24.58.225]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 14BC324655; Wed, 18 Nov 2020 02:18:38 +0000 (UTC) Date: Tue, 17 Nov 2020 21:18:36 -0500 From: Steven Rostedt To: LKML Cc: Mathieu Desnoyers , Matt Mullins , paulmck , Ingo Molnar , Alexei Starovoitov , Daniel Borkmann , Dmitry Vyukov , Martin KaFai Lau , Song Liu , Yonghong Song , Andrii Nakryiko , John Fastabend , KP Singh , netdev , bpf , Kees Cook , Peter Zijlstra , Josh Poimboeuf Subject: [PATCH v2] tracepoint: Do not fail unregistering a probe due to memory allocation Message-ID: <20201117211836.54acaef2@oasis.local.home> X-Mailer: Claws Mail 3.17.3 (GTK+ 2.24.32; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Steven Rostedt (VMware)" The list of tracepoint callbacks is managed by an array that is protected by RCU. To update this array, a new array is allocated, the updates are copied over to the new array, and then the list of functions for the tracepoint is switched over to the new array. After a completion of an RCU grace period, the old array is freed. This process happens for both adding a callback as well as removing one. But on removing a callback, if the new array fails to be allocated, the callback is not removed, and may be used after it is freed by the clients of the tracepoint. There's really no reason to fail if the allocation for a new array fails when removing a function. Instead, the function can simply be replaced by a stub that will be ignored in the callback loop, and it will be cleaned up on the next modification of the array. Link: https://lore.kernel.org/r/20201115055256.65625-1-mmullins@mmlx.us Link: https://lkml.kernel.org/r/20201116175107.02db396d@gandalf.local.home Cc: Mathieu Desnoyers Cc: Ingo Molnar Cc: Alexei Starovoitov Cc: Daniel Borkmann Cc: Dmitry Vyukov Cc: Martin KaFai Lau Cc: Song Liu Cc: Yonghong Song Cc: Andrii Nakryiko Cc: John Fastabend Cc: KP Singh Cc: netdev Cc: bpf Cc: Kees Cook Cc: stable@vger.kernel.org Fixes: 97e1c18e8d17b ("tracing: Kernel Tracepoints") Reported-by: syzbot+83aa762ef23b6f0d1991@syzkaller.appspotmail.com Reported-by: syzbot+d29e58bb557324e55e5e@syzkaller.appspotmail.com Reported-by: Matt Mullins Signed-off-by: Steven Rostedt (VMware) --- Changes since v1: Use 1L value for stub function, and ignore calling it. include/linux/tracepoint.h | 9 ++++- kernel/tracepoint.c | 80 +++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 0f21617f1a66..2e06e05b9d2a 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -33,6 +33,8 @@ struct trace_eval_map { #define TRACEPOINT_DEFAULT_PRIO 10 +#define TRACEPOINT_STUB ((void *)0x1L) + extern struct srcu_struct tracepoint_srcu; extern int @@ -310,7 +312,12 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) do { \ it_func = (it_func_ptr)->func; \ __data = (it_func_ptr)->data; \ - ((void(*)(void *, proto))(it_func))(__data, args); \ + /* \ + * Removed functions that couldn't be allocated \ + * are replaced with TRACEPOINT_STUB. \ + */ \ + if (likely(it_func != TRACEPOINT_STUB)) \ + ((void(*)(void *, proto))(it_func))(__data, args); \ } while ((++it_func_ptr)->func); \ return 0; \ } \ diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 3f659f855074..20ce78b3f578 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -53,10 +53,10 @@ struct tp_probes { struct tracepoint_func probes[]; }; -static inline void *allocate_probes(int count) +static inline void *allocate_probes(int count, gfp_t extra_flags) { struct tp_probes *p = kmalloc(struct_size(p, probes, count), - GFP_KERNEL); + GFP_KERNEL | extra_flags); return p == NULL ? NULL : p->probes; } @@ -131,6 +131,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, { struct tracepoint_func *old, *new; int nr_probes = 0; + int stub_funcs = 0; int pos = -1; if (WARN_ON(!tp_func->func)) @@ -147,14 +148,34 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, if (old[nr_probes].func == tp_func->func && old[nr_probes].data == tp_func->data) return ERR_PTR(-EEXIST); + if (old[nr_probes].func == TRACEPOINT_STUB) + stub_funcs++; } } - /* + 2 : one for new probe, one for NULL func */ - new = allocate_probes(nr_probes + 2); + /* + 2 : one for new probe, one for NULL func - stub functions */ + new = allocate_probes(nr_probes + 2 - stub_funcs, 0); if (new == NULL) return ERR_PTR(-ENOMEM); if (old) { - if (pos < 0) { + if (stub_funcs) { + /* Need to copy one at a time to remove stubs */ + int probes = 0; + + pos = -1; + for (nr_probes = 0; old[nr_probes].func; nr_probes++) { + if (old[nr_probes].func == TRACEPOINT_STUB) + continue; + if (pos < 0 && old[nr_probes].prio < prio) + pos = probes++; + new[probes++] = old[nr_probes]; + } + nr_probes = probes; + if (pos < 0) + pos = probes; + else + nr_probes--; /* Account for insertion */ + + } else if (pos < 0) { pos = nr_probes; memcpy(new, old, nr_probes * sizeof(struct tracepoint_func)); } else { @@ -188,8 +209,9 @@ static void *func_remove(struct tracepoint_func **funcs, /* (N -> M), (N > 1, M >= 0) probes */ if (tp_func->func) { for (nr_probes = 0; old[nr_probes].func; nr_probes++) { - if (old[nr_probes].func == tp_func->func && - old[nr_probes].data == tp_func->data) + if ((old[nr_probes].func == tp_func->func && + old[nr_probes].data == tp_func->data) || + old[nr_probes].func == TRACEPOINT_STUB) nr_del++; } } @@ -207,15 +229,33 @@ static void *func_remove(struct tracepoint_func **funcs, int j = 0; /* N -> M, (N > 1, M > 0) */ /* + 1 for NULL */ - new = allocate_probes(nr_probes - nr_del + 1); - if (new == NULL) - return ERR_PTR(-ENOMEM); - for (i = 0; old[i].func; i++) - if (old[i].func != tp_func->func - || old[i].data != tp_func->data) - new[j++] = old[i]; - new[nr_probes - nr_del].func = NULL; - *funcs = new; + new = allocate_probes(nr_probes - nr_del + 1, __GFP_NOFAIL); + if (new) { + for (i = 0; old[i].func; i++) + if ((old[i].func != tp_func->func + || old[i].data != tp_func->data) + && old[i].func != TRACEPOINT_STUB) + new[j++] = old[i]; + new[nr_probes - nr_del].func = NULL; + *funcs = new; + } else { + /* + * Failed to allocate, replace the old function + * with TRACEPOINT_STUB. + */ + for (i = 0; old[i].func; i++) + if (old[i].func == tp_func->func && + old[i].data == tp_func->data) { + old[i].func = TRACEPOINT_STUB; + /* Set the prio to the next event. */ + if (old[i + 1].func) + old[i].prio = + old[i + 1].prio; + else + old[i].prio = -1; + } + *funcs = old; + } } debug_print_probes(*funcs); return old; @@ -295,10 +335,12 @@ static int tracepoint_remove_func(struct tracepoint *tp, tp_funcs = rcu_dereference_protected(tp->funcs, lockdep_is_held(&tracepoints_mutex)); old = func_remove(&tp_funcs, func); - if (IS_ERR(old)) { - WARN_ON_ONCE(PTR_ERR(old) != -ENOMEM); + if (WARN_ON_ONCE(IS_ERR(old))) return PTR_ERR(old); - } + + if (tp_funcs == old) + /* Failed allocating new tp_funcs, replaced func with stub */ + return 0; if (!tp_funcs) { /* Removed last function */ -- 2.25.4