From nobody Mon Sep 15 18:04:06 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00723C5479D for ; Wed, 11 Jan 2023 10:44:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235850AbjAKKnw (ORCPT ); Wed, 11 Jan 2023 05:43:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58544 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238079AbjAKKnY (ORCPT ); Wed, 11 Jan 2023 05:43:24 -0500 Received: from szxga02-in.huawei.com (szxga02-in.huawei.com [45.249.212.188]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF52F2718; Wed, 11 Jan 2023 02:43:22 -0800 (PST) Received: from kwepemm600006.china.huawei.com (unknown [172.30.72.55]) by szxga02-in.huawei.com (SkyGuard) with ESMTP id 4NsPPG1V8GzRqq2; Wed, 11 Jan 2023 18:41:38 +0800 (CST) Received: from huawei.com (10.44.142.108) by kwepemm600006.china.huawei.com (7.193.23.105) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.34; Wed, 11 Jan 2023 18:43:19 +0800 From: To: , , , , , , CC: , , , , , , , Longjun Luo Subject: [PATCH] uprobes: list all active uprobes in the system Date: Wed, 11 Jan 2023 18:42:24 +0800 Message-ID: <20230111104224.1934567-1-luolongjun@huawei.com> X-Mailer: git-send-email 2.38.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.44.142.108] X-ClientProxiedBy: dggems701-chm.china.huawei.com (10.3.19.178) To kwepemm600006.china.huawei.com (7.193.23.105) X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Longjun Luo Since uprobes will replace instructions in the process memory, it is necessary to provide one way to list all active uprobes. One can access this file through /sys/kernel/debug/uprobes/list. Output looks like this =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D inode+offset/ref_ctr_offset 4160760670+0x30a10/0x0 ref 1 register_rwsem (unlocked) consumer_rwsem (unlocked) consumers-1 handler 0xffffffffc03ee02e(handler) uprobe ret_handler 0x0000000000000000( ) filter 0xffffffffc03ee010(filter) uprobe consumers-2 handler 0xffffffffc03e902e(handler_1) uprobe_1 ret_handler 0x0000000000000000( ) filter 0xffffffffc03e9010(filter_1) uprobe_1 Signed-off-by: Longjun Luo --- kernel/events/uprobes.c | 120 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 11 deletions(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index d9e357b7e17c..2fa9b910abc7 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include =20 #include =20 @@ -40,7 +43,7 @@ static struct rb_root uprobes_tree =3D RB_ROOT; */ #define no_uprobe_events() RB_EMPTY_ROOT(&uprobes_tree) =20 -static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ +static DECLARE_RWSEM(uprobes_treelock); /* serialize rbtree access */ =20 #define UPROBES_HASH_SZ 13 /* serialize uprobe->pending_list */ @@ -672,9 +675,9 @@ static struct uprobe *find_uprobe(struct inode *inode, = loff_t offset) { struct uprobe *uprobe; =20 - spin_lock(&uprobes_treelock); + down_read(&uprobes_treelock); uprobe =3D __find_uprobe(inode, offset); - spin_unlock(&uprobes_treelock); + up_read(&uprobes_treelock); =20 return uprobe; } @@ -704,9 +707,9 @@ static struct uprobe *insert_uprobe(struct uprobe *upro= be) { struct uprobe *u; =20 - spin_lock(&uprobes_treelock); + down_write(&uprobes_treelock); u =3D __insert_uprobe(uprobe); - spin_unlock(&uprobes_treelock); + up_write(&uprobes_treelock); =20 return u; } @@ -938,9 +941,9 @@ static void delete_uprobe(struct uprobe *uprobe) if (WARN_ON(!uprobe_is_active(uprobe))) return; =20 - spin_lock(&uprobes_treelock); + down_write(&uprobes_treelock); rb_erase(&uprobe->rb_node, &uprobes_tree); - spin_unlock(&uprobes_treelock); + up_write(&uprobes_treelock); RB_CLEAR_NODE(&uprobe->rb_node); /* for uprobe_is_active() */ put_uprobe(uprobe); } @@ -1301,7 +1304,7 @@ static void build_probe_list(struct inode *inode, min =3D vaddr_to_offset(vma, start); max =3D min + (end - start) - 1; =20 - spin_lock(&uprobes_treelock); + down_read(&uprobes_treelock); n =3D find_node_in_range(inode, min, max); if (n) { for (t =3D n; t; t =3D rb_prev(t)) { @@ -1319,7 +1322,7 @@ static void build_probe_list(struct inode *inode, get_uprobe(u); } } - spin_unlock(&uprobes_treelock); + up_read(&uprobes_treelock); } =20 /* @vma contains reference counter, not the probed instruction. */ @@ -1410,9 +1413,9 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned = long start, unsigned long e min =3D vaddr_to_offset(vma, start); max =3D min + (end - start) - 1; =20 - spin_lock(&uprobes_treelock); + down_read(&uprobes_treelock); n =3D find_node_in_range(inode, min, max); - spin_unlock(&uprobes_treelock); + up_read(&uprobes_treelock); =20 return !!n; } @@ -2357,3 +2360,98 @@ void __init uprobes_init(void) =20 BUG_ON(register_die_notifier(&uprobe_exception_nb)); } + +#ifdef CONFIG_DEBUG_FS + +static void *uprobe_seq_start(struct seq_file *m, loff_t *pos) +{ + loff_t num =3D *pos; + struct rb_node *rbp; + + down_read(&uprobes_treelock); + for (rbp =3D rb_first(&uprobes_tree); rbp; rbp =3D rb_next(rbp)) { + if (num-- =3D=3D 0) + return rbp; + } + + return NULL; +} + +static void *uprobe_seq_next(struct seq_file *f, void *v, loff_t *pos) +{ + ++*pos; + return (void *)rb_next(v); +} + +static void uprobe_seq_stop(struct seq_file *f, void *v) +{ + up_read(&uprobes_treelock); +} + +static int show_uprobe(struct seq_file *m, void *v) +{ + struct uprobe *uprobe =3D rb_entry_safe(v, struct uprobe, rb_node); + struct uprobe_consumer *con; + char *modname, namebuf[KSYM_NAME_LEN]; + const char *sym; + unsigned int cnt =3D 1; + + seq_printf(m, "inode+offset/ref_ctr_offset %lu+0x%llx/0x%llx\n", + uprobe->inode->i_ino, uprobe->offset, uprobe->ref_ctr_offset); + seq_printf(m, " ref %u\n", + refcount_read(&uprobe->ref)); + seq_printf(m, " register_rwsem %s\n", + rwsem_is_locked(&uprobe->register_rwsem)?"(locked)":"(unlocked)"); + seq_printf(m, " consumer_rwsem %s\n", + rwsem_is_locked(&uprobe->consumer_rwsem)?"(locked)":"(unlocked)"); + + /* lock consumer_rwsem when hold uprobes_treelock */ + down_read(&uprobe->consumer_rwsem); + for (con =3D uprobe->consumers; con; con =3D con->next, cnt++) { + seq_printf(m, " consumers-%u\n", cnt); + + modname =3D NULL; + sym =3D kallsyms_lookup((unsigned long)con->handler, NULL, NULL, + &modname, namebuf); + seq_printf(m, " handler 0x%px(%s) %s\n", + con->handler, (sym ? sym : " "), (modname ? modname : " ")); + + modname =3D NULL; + sym =3D kallsyms_lookup((unsigned long)con->ret_handler, NULL, NULL, + &modname, namebuf); + seq_printf(m, " ret_handler 0x%px(%s) %s\n", + con->ret_handler, (sym ? sym : " "), (modname ? modname : " ")); + + modname =3D NULL; + sym =3D kallsyms_lookup((unsigned long)con->filter, NULL, NULL, + &modname, namebuf); + seq_printf(m, " filter 0x%px(%s) %s\n", + con->filter, (sym ? sym : " "), (modname ? modname : " ")); + } + up_read(&uprobe->consumer_rwsem); + + return 0; +} + +static const struct seq_operations uprobes_sops =3D { + .start =3D uprobe_seq_start, + .next =3D uprobe_seq_next, + .stop =3D uprobe_seq_stop, + .show =3D show_uprobe, +}; + +DEFINE_SEQ_ATTRIBUTE(uprobes); + +static int __init debugfs_uprobe_init(void) +{ + struct dentry *dir; + + dir =3D debugfs_create_dir("uprobes", NULL); + + debugfs_create_file("list", 0400, dir, NULL, &uprobes_fops); + + return 0; +} +late_initcall(debugfs_uprobe_init); + +#endif /* CONFIG_DEBUG_FS */ --=20 2.38.1