Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp3545150pxb; Mon, 16 Nov 2020 18:36:38 -0800 (PST) X-Google-Smtp-Source: ABdhPJyWcSXgoeSaLqdPImWlsZRViZWD+xIS8/j5VcQzeQfn+YFj9kNTrIDC/xnd0KNtSqQveZ9e X-Received: by 2002:a50:9993:: with SMTP id m19mr18334359edb.99.1605580597797; Mon, 16 Nov 2020 18:36:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1605580597; cv=none; d=google.com; s=arc-20160816; b=PsIlr2vxb0N7yD+agX5nMywtTnbI9ACCvJhlaZX2tRX525MoQavjjerbGUqbTLqooq +tnI/hK43knph3zqMfMBp4KTIsU9bAlu37WpKLLQH/gKlZaUTH81wFwCVwq9YrKSZ3UJ KOjzuEYA3IDXn0R0TxgVmLJLjNh62fX1uF5FsEB8YojSxJ7I2VvlK+Iz4fWBfWzUjbd5 45OHKoveu/urLD3tRCsZUXSVBm0XzphFwNLF/JXubvFQK2gkQEe4y1LiE6HxY1abLwEO FoYan2OPKrZ83269skaSt8ODi3KcPCW09Slt2QEx6q7U3mUuEZYB0mItvo+tMWJPiMtF tBQA== 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=ccwitNBuwv1pNJ1KZ/Jg86uoEw64rJU0Jv+NzUxlrtY=; b=W8gTLyMMQUNayRflNOjlBl8pojY0xxLLfy9Uevs37AaSmIKXxFAq0aqYDAr078Jz3O AUWeH06h9TTk1kB8n8wJVenhOTU6S8ZaiKtWKDX5kWnTrq1uuXsa/3KgBOQGXLBwacPL ypu92myH5xYmRoU93Wdpqfbv7oNW50SngSgqzFTTQZ/9rW2NknW+MxSpxARitapWwEY1 j8+q0eRcC2sjCRYP1Ai0q8IgTRbIGYZz4xUCj3bCtEompVVDsTT3bGWzX1tL1uNz9Tk/ jtYkhfk/04aE4JEvtC9FtJe0d3MPTgZm34bv+SfpyjDPWluOUWvNxI7H4nU8ZPaNV9Sr JcSA== 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 j10si13242182edt.142.2020.11.16.18.36.14; Mon, 16 Nov 2020 18:36:37 -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 S1727900AbgKPWvM (ORCPT + 99 others); Mon, 16 Nov 2020 17:51:12 -0500 Received: from mail.kernel.org ([198.145.29.99]:50556 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726204AbgKPWvM (ORCPT ); Mon, 16 Nov 2020 17:51:12 -0500 Received: from gandalf.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 75A362244C; Mon, 16 Nov 2020 22:51:09 +0000 (UTC) Date: Mon, 16 Nov 2020 17:51:07 -0500 From: Steven Rostedt To: LKML Cc: Mathieu Desnoyers , Matt Mullins , 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 Subject: [PATCH] tracepoint: Do not fail unregistering a probe due to memory allocation Message-ID: <20201116175107.02db396d@gandalf.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 [ Kees, I added you because you tend to know about these things. Is it OK to assign a void func(void) that doesn't do anything and returns nothing to a function pointer that could be call with parameters? We need to add stubs for tracepoints when we fail to allocate a new array on removal of a callback, but the callbacks do have arguments, but the stub called does not have arguments. Matt, Does this patch fix the error your patch was trying to fix? ] 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 function that could be cleaned up on the next modification of the array. That is, instead of calling the function registered to the tracepoint, it would call a stub function in its place. Link: https://lore.kernel.org/r/20201115055256.65625-1-mmullins@mmlx.us 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 --- diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 3f659f855074..774b3733cbbe 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -53,10 +53,16 @@ struct tp_probes { struct tracepoint_func probes[]; }; -static inline void *allocate_probes(int count) +/* Called in removal of a func but failed to allocate a new tp_funcs */ +static void tp_stub_func(void) +{ + return; +} + +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 +137,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 +154,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 == tp_stub_func) + 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 == tp_stub_func) + 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 +215,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 == tp_stub_func) nr_del++; } } @@ -207,15 +235,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 != tp_stub_func) + new[j++] = old[i]; + new[nr_probes - nr_del].func = NULL; + *funcs = new; + } else { + /* + * Failed to allocate, replace the old function + * with calls to tp_stub_func. + */ + for (i = 0; old[i].func; i++) + if (old[i].func == tp_func->func && + old[i].data == tp_func->data) { + old[i].func = tp_stub_func; + /* 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 +341,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 */