Received: by 2002:a05:6a10:8c0a:0:0:0:0 with SMTP id go10csp591721pxb; Wed, 27 Jan 2021 16:05:35 -0800 (PST) X-Google-Smtp-Source: ABdhPJxRBe52hXH1SSvPbIP8a550+q3t8qPAjT+ie5g4VUmkhGCraTmQRj6LgwbidgUpeHchcRMk X-Received: by 2002:a17:906:388b:: with SMTP id q11mr8706976ejd.421.1611792335604; Wed, 27 Jan 2021 16:05:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1611792335; cv=none; d=google.com; s=arc-20160816; b=rwxMxDwIAUIehIpK1StTYl1clrNraEaNAb3xxoHW+bIdmK+iUdzRpi45RhbJkY7VxK evN7XIdmh+sdeBWvGU1DSk3LeJINVvbFAZrsUTOs5xAaiXtHuYj4W1GuFe8YTlDonXui DrFKFGf8hSxAg8HpPV4GPvDYfYYrvWG6sDBkkygGtK+FqdTkK+Y6jJYDwMZXqd236jYx yPyRX27M9uhfPM61izwnF6sk6LwJaz/SFbixwoz3kQUwHzCQ1cIskhsjySgZ8yjnPGFr YsdHkoSrF5V5DHkpT6nLhJDRCNY6SmSSao6eYiUpy3vD1PghmPXHTiChwCfzlFWICVrJ COFQ== 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 :user-agent:message-id:date:subject:cc:to:from:dkim-signature; bh=1F4YE6M8F1Ys/eL7c5Q7WT3KdbkM7rzF+mQ3Usep9PQ=; b=eqXn3oR34JOwv/1aUTaA394jwEtPBEbSdb93Jki8SGKaioj6DD2DXzrwepddKhBHet 776z+s7QUpAmxfylcwWMJdLI/L7cYwM7tyy2/0I1xmh2RQluGMUXJ6mb9ALx4z7Zw9sP /GH1IrPgvNc2uncCbSL6d3JvZL5HRRBHBn19kky3J9QnJgaJtcSw+TEX8AZY+a37YGVb ZQhbw2nv5dRSM7nh4y9UfLa783U8i6qr+50AZ35aMU5aMpU6oCGh9BGSSjFES1iJsrDt XLs/TUnUrtJxEZHNod4u34BXKxVyLkKu7hQuFfQzAfj/8gK7cwm2YHpOfJF7LmJuO9Jm lxfQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=dQe+BrVV; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id d24si1223499edt.102.2021.01.27.16.05.11; Wed, 27 Jan 2021 16:05: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; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=dQe+BrVV; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234212AbhA0Pm7 (ORCPT + 99 others); Wed, 27 Jan 2021 10:42:59 -0500 Received: from mail.kernel.org ([198.145.29.99]:36006 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236402AbhA0Pig (ORCPT ); Wed, 27 Jan 2021 10:38:36 -0500 Received: by mail.kernel.org (Postfix) with ESMTPSA id 0C1E5207D2; Wed, 27 Jan 2021 15:37:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1611761875; bh=4YbAgCX4nMowdsDHgYOQhVgDy0O02P5NFXjKWDCxUyw=; h=From:To:Cc:Subject:Date:From; b=dQe+BrVVF4/0RnhQzzQ8WilNvbqtHniLzKQV4zjvWA/yENHQ5gN1NhoaAdfzkEKhH twDQpYpD8Gj2rL9AIm4ceD+SYeJ1U3fmsi0qxfcKn62JPo3Feq/n6PVu7M0tMVOCk8 OXc4w+NUl40qSZ0k/StbbVmThKnwtVHN7aQ4jalOcbsvnj3TLbyDgf0EemQGrxyLan H/Ik2p1KZz6MnG55kyoaSFs4K4G8z2GHj1cr76IJAAYfjtl3PIgHASiqhqFITqWlXl nOAauwGT4St3wjEfCD/c3K/snw9K8JTktwig3LTKEwrClUxucrgfkb8dTKEFtRZnGJ 7TA1hc7ye5ARA== From: Masami Hiramatsu To: Steven Rostedt Cc: Jianlin Lv , Oleg Nesterov , Ingo Molnar , linux-kernel@vger.kernel.org, mhiramat@kernel.org Subject: [PATCH] tracing/kprobe: Fix to support kretprobe events on unloaded modules Date: Thu, 28 Jan 2021 00:37:51 +0900 Message-Id: <161176187132.1067016.8118042342894378981.stgit@devnote2> X-Mailer: git-send-email 2.25.1 User-Agent: StGit/0.19 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Fix kprobe_on_func_entry() returns error code instead of false so that register_kretprobe() can return an appropriate error code. append_trace_kprobe() expects the kprobe registration returns -ENOENT when the target symbol is not found, and it checks whether the target module is unloaded or not. If the target module doesn't exist, it defers to probe the target symbol until the module is loaded. However, since register_kretprobe() returns -EINVAL instead of -ENOENT in that case, it always fail on putting the kretprobe event on unloaded modules. e.g. Kprobe event: /sys/kernel/debug/tracing # echo p xfs:xfs_end_io >> kprobe_events [ 16.515574] trace_kprobe: This probe might be able to register after target module is loaded. Continue. Kretprobe event: (p -> r) /sys/kernel/debug/tracing # echo r xfs:xfs_end_io >> kprobe_events sh: write error: Invalid argument /sys/kernel/debug/tracing # cat error_log [ 41.122514] trace_kprobe: error: Failed to register probe event Command: r xfs:xfs_end_io ^ To fix this bug, change kprobe_on_func_entry() to detect symbol lookup failure and return -ENOENT in that case. Otherwise it returns -EINVAL or 0 (succeeded, given address is on the entry). Reported-by: Jianlin Lv Signed-off-by: Masami Hiramatsu --- include/linux/kprobes.h | 2 +- kernel/kprobes.c | 34 +++++++++++++++++++++++++--------- kernel/trace/trace_kprobe.c | 10 ++++++---- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index b3a36b0cfc81..1883a4a9f16a 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -266,7 +266,7 @@ extern void kprobes_inc_nmissed_count(struct kprobe *p); extern bool arch_within_kprobe_blacklist(unsigned long addr); extern int arch_populate_kprobe_blacklist(void); extern bool arch_kprobe_on_func_entry(unsigned long offset); -extern bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset); +extern int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset); extern bool within_kprobe_blacklist(unsigned long addr); extern int kprobe_add_ksym_blacklist(unsigned long entry); diff --git a/kernel/kprobes.c b/kernel/kprobes.c index f7fb5d135930..1a5bc321e0a5 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1954,29 +1954,45 @@ bool __weak arch_kprobe_on_func_entry(unsigned long offset) return !offset; } -bool kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset) +/** + * kprobe_on_func_entry() -- check whether given address is function entry + * @addr: Target address + * @sym: Target symbol name + * @offset: The offset from the symbol or the address + * + * This checks whether the given @addr+@offset or @sym+@offset is on the + * function entry address or not. + * This returns 0 if it is the function entry, or -EINVAL if it is not. + * And also it returns -ENOENT if it fails the symbol or address lookup. + * Caller must pass @addr or @sym (either one must be NULL), or this + * returns -EINVAL. + */ +int kprobe_on_func_entry(kprobe_opcode_t *addr, const char *sym, unsigned long offset) { kprobe_opcode_t *kp_addr = _kprobe_addr(addr, sym, offset); if (IS_ERR(kp_addr)) - return false; + return PTR_ERR(kp_addr); - if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset) || - !arch_kprobe_on_func_entry(offset)) - return false; + if (!kallsyms_lookup_size_offset((unsigned long)kp_addr, NULL, &offset)) + return -ENOENT; - return true; + if (!arch_kprobe_on_func_entry(offset)) + return -EINVAL; + + return 0; } int register_kretprobe(struct kretprobe *rp) { - int ret = 0; + int ret; struct kretprobe_instance *inst; int i; void *addr; - if (!kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset)) - return -EINVAL; + ret = kprobe_on_func_entry(rp->kp.addr, rp->kp.symbol_name, rp->kp.offset); + if (ret) + return ret; if (kretprobe_blacklist_size) { addr = kprobe_addr(&rp->kp); diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index e6fba1798771..56c7fbff7bd7 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -221,9 +221,9 @@ bool trace_kprobe_on_func_entry(struct trace_event_call *call) { struct trace_kprobe *tk = trace_kprobe_primary_from_call(call); - return tk ? kprobe_on_func_entry(tk->rp.kp.addr, + return tk ? (kprobe_on_func_entry(tk->rp.kp.addr, tk->rp.kp.addr ? NULL : tk->rp.kp.symbol_name, - tk->rp.kp.addr ? 0 : tk->rp.kp.offset) : false; + tk->rp.kp.addr ? 0 : tk->rp.kp.offset) == 0) : false; } bool trace_kprobe_error_injectable(struct trace_event_call *call) @@ -828,9 +828,11 @@ static int trace_kprobe_create(int argc, const char *argv[]) } if (is_return) flags |= TPARG_FL_RETURN; - if (kprobe_on_func_entry(NULL, symbol, offset)) + ret = kprobe_on_func_entry(NULL, symbol, offset); + if (ret == 0) flags |= TPARG_FL_FENTRY; - if (offset && is_return && !(flags & TPARG_FL_FENTRY)) { + /* Defer the ENOENT case until register kprobe */ + if (ret == -EINVAL && is_return) { trace_probe_log_err(0, BAD_RETPROBE); goto parse_error; }