From patchwork Tue Jun 23 12:07:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 217326 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, UNPARSEABLE_RELAY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4FFBC433E0 for ; Tue, 23 Jun 2020 12:10:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 98D8920707 for ; Tue, 23 Jun 2020 12:10:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b="zRnlpoCO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732725AbgFWMKr (ORCPT ); Tue, 23 Jun 2020 08:10:47 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:51566 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732364AbgFWMKV (ORCPT ); Tue, 23 Jun 2020 08:10:21 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC7kBj165372; Tue, 23 Jun 2020 12:09:21 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=Yr077G7HHvjaApviN2UZnD5/PNMj9ClVOffuYqAVEYI=; b=zRnlpoCOjn3AZONQ+EfPSxkBX5SJOxmMVsOUrAYz9YDbcUrB4aL6LPCT8fNBs32SjbWM D7CUQLyUwLFqspiWf2kaLQyWELpmxO70TZyho50QXcN3A9jfPcu/94KGLclAiRdiOtv8 EbzbmkTzNvyrLGIAZTBGwi8kZWDj598q+0QdWujHUOTmeRZ9PGxxyyg5p0xlInYnVd5S igeoOKCGtapQbBL2ca/+b4Md76hfrf1PHO+TJIImQofUWAMYCql5kUaHJtJGtbTzuMH2 TkoGKgAlwqz1icdPRiyXU5ybsbwIZwPyu4Ihq4fcwVV8FP5p4nUok/9f/vAmo0TVi6q3 Bw== Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by userp2120.oracle.com with ESMTP id 31sebbmttt-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jun 2020 12:09:20 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC9CAF063642; Tue, 23 Jun 2020 12:09:20 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by userp3030.oracle.com with ESMTP id 31svcwnjpt-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 23 Jun 2020 12:09:20 +0000 Received: from abhmp0017.oracle.com (abhmp0017.oracle.com [141.146.116.23]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id 05NC9ILm018697; Tue, 23 Jun 2020 12:09:18 GMT Received: from localhost.uk.oracle.com (/10.175.166.3) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 23 Jun 2020 12:09:18 +0000 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, yhs@fb.com, andriin@fb.com, arnaldo.melo@gmail.com Cc: kafai@fb.com, songliubraving@fb.com, john.fastabend@gmail.com, kpsingh@chromium.org, linux@rasmusvillemoes.dk, joe@perches.com, pmladek@suse.com, rostedt@goodmis.org, sergey.senozhatsky@gmail.com, andriy.shevchenko@linux.intel.com, corbet@lwn.net, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v3 bpf-next 4/8] printk: add type-printing %pT format specifier which uses BTF Date: Tue, 23 Jun 2020 13:07:07 +0100 Message-Id: <1592914031-31049-5-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> References: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 bulkscore=0 adultscore=0 malwarescore=0 suspectscore=0 phishscore=0 mlxscore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 malwarescore=0 phishscore=0 adultscore=0 impostorscore=0 cotscore=-2147483648 mlxscore=0 suspectscore=0 mlxlogscore=999 bulkscore=0 lowpriorityscore=0 clxscore=1015 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org printk supports multiple pointer object type specifiers (printing netdev features etc). Extend this support using BTF to cover arbitrary types. "%pT" specifies the typed format, and the pointer argument is a "struct btf_ptr *" where struct btf_ptr is as follows: struct btf_ptr { void *ptr; const char *type; u32 id; }; Either the "type" string ("struct sk_buff") or the BTF "id" can be used to identify the type to use in displaying the associated "ptr" value. A convenience function to create and point at the struct is provided: printk(KERN_INFO "%pT", BTF_PTR_TYPE(skb, struct sk_buff)); When invoked, BTF information is used to traverse the sk_buff * and display it. Support is present for structs, unions, enums, typedefs and core types (though in the latter case there's not much value in using this feature of course). Default output is indented, but compact output can be specified via the 'c' option. Type names/member values can be suppressed using the 'N' option. Zero values are not displayed by default but can be using the '0' option. Pointer values are obfuscated unless the 'x' option is specified. As an example: struct sk_buff *skb = alloc_skb(64, GFP_KERNEL); pr_info("%pT", BTF_PTR_TYPE(skb, struct sk_buff)); ...gives us: (struct sk_buff){ .transport_header = (__u16)65535, .mac_header = (__u16)65535, .end = (sk_buff_data_t)192, .head = (unsigned char *)0x000000006b71155a, .data = (unsigned char *)0x000000006b71155a, .truesize = (unsigned int)768, .users = (refcount_t){ .refs = (atomic_t){ .counter = (int)1, }, }, .extensions = (struct skb_ext *)0x00000000f486a130, } printk output is truncated at 1024 bytes. For cases where overflow is likely, the compact/no type names display modes may be used. Signed-off-by: Alan Maguire i --- Documentation/core-api/printk-formats.rst | 17 ++++++ include/linux/btf.h | 3 +- include/linux/printk.h | 16 +++++ lib/vsprintf.c | 98 +++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index 8c9aba2..8f255d0 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -563,6 +563,23 @@ For printing netdev_features_t. Passed by reference. +BTF-based printing of pointer data +---------------------------------- +If '%pT' is specified, use the struct btf_ptr * along with kernel vmlinux +BPF Type Format (BTF) to show the typed data. For example, specifying + + printk(KERN_INFO "%pT", BTF_PTR_TYPE(skb, struct_sk_buff)); + +will utilize BTF information to traverse the struct sk_buff * and display it. + +Supported modifers are + 'c' compact output (no indentation, newlines etc) + 'N' do not show type names + 'u' unsafe printing; probe_kernel_read() is not used to copy data safely + before use + 'x' show raw pointers (no obfuscation) + '0' show zero-valued data (it is not shown by default) + Thanks ====== diff --git a/include/linux/btf.h b/include/linux/btf.h index a8a4563..e8dbf0c 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -172,10 +172,11 @@ static inline const struct btf_member *btf_type_member(const struct btf_type *t) return (const struct btf_member *)(t + 1); } +struct btf *btf_parse_vmlinux(void); + #ifdef CONFIG_BPF_SYSCALL const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); const char *btf_name_by_offset(const struct btf *btf, u32 offset); -struct btf *btf_parse_vmlinux(void); struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog); #else static inline const struct btf_type *btf_type_by_id(const struct btf *btf, diff --git a/include/linux/printk.h b/include/linux/printk.h index fc8f03c..8f8f5d2 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -618,4 +618,20 @@ static inline void print_hex_dump_debug(const char *prefix_str, int prefix_type, #define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ print_hex_dump_debug(prefix_str, prefix_type, 16, 1, buf, len, true) +/** + * struct btf_ptr is used for %pT (typed pointer) display; the + * additional type string/BTF id are used to render the pointer + * data as the appropriate type. + */ +struct btf_ptr { + void *ptr; + const char *type; + u32 id; +}; + +#define BTF_PTR_TYPE(ptrval, typeval) \ + (&((struct btf_ptr){.ptr = ptrval, .type = #typeval})) + +#define BTF_PTR_ID(ptrval, idval) \ + (&((struct btf_ptr){.ptr = ptrval, .id = idval})) #endif diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 259e558..c0d209d 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -44,6 +44,7 @@ #ifdef CONFIG_BLOCK #include #endif +#include #include "../mm/internal.h" /* For the trace_print_flags arrays */ @@ -2092,6 +2093,87 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode, return widen_string(buf, buf - buf_start, end, spec); } +#define btf_modifier_flag(c) (c == 'c' ? BTF_SHOW_COMPACT : \ + c == 'N' ? BTF_SHOW_NONAME : \ + c == 'x' ? BTF_SHOW_PTR_RAW : \ + c == 'u' ? BTF_SHOW_UNSAFE : \ + c == '0' ? BTF_SHOW_ZERO : 0) + +static noinline_for_stack +char *btf_string(char *buf, char *end, void *ptr, struct printf_spec spec, + const char *fmt) +{ + struct btf_ptr *bp = (struct btf_ptr *)ptr; + u8 btf_kind = BTF_KIND_TYPEDEF; + const struct btf_type *t; + const struct btf *btf; + char *buf_start = buf; + const char *btf_type; + u64 flags = 0, mod; + s32 btf_id; + + if (check_pointer(&buf, end, ptr, spec)) + return buf; + + if (check_pointer(&buf, end, bp->ptr, spec)) + return buf; + + while (isalnum(*fmt)) { + mod = btf_modifier_flag(*fmt); + if (!mod) + break; + flags |= mod; + fmt++; + } + + btf = bpf_get_btf_vmlinux(); + if (IS_ERR_OR_NULL(btf)) + return ptr_to_id(buf, end, bp->ptr, spec); + + if (bp->type != NULL) { + btf_type = bp->type; + + if (strncmp(bp->type, "struct ", strlen("struct ")) == 0) { + btf_kind = BTF_KIND_STRUCT; + btf_type += strlen("struct "); + } else if (strncmp(btf_type, "union ", strlen("union ")) == 0) { + btf_kind = BTF_KIND_UNION; + btf_type += strlen("union "); + } else if (strncmp(btf_type, "enum ", strlen("enum ")) == 0) { + btf_kind = BTF_KIND_ENUM; + btf_type += strlen("enum "); + } + + if (strlen(btf_type) == 0) + return ptr_to_id(buf, end, bp->ptr, spec); + + /* + * Assume type specified is a typedef as there's not much + * benefit in specifying int types other than wasting time + * on BTF lookups; we optimize for the most useful path. + * + * Fall back to BTF_KIND_INT if this fails. + */ + btf_id = btf_find_by_name_kind(btf, btf_type, btf_kind); + if (btf_id < 0) + btf_id = btf_find_by_name_kind(btf, btf_type, + BTF_KIND_INT); + } else if (bp->id > 0) + btf_id = bp->id; + else + return ptr_to_id(buf, end, bp->ptr, spec); + + if (btf_id > 0) + t = btf_type_by_id(btf, btf_id); + if (btf_id <= 0 || !t) + return ptr_to_id(buf, end, bp->ptr, spec); + + buf += btf_type_snprintf_show(btf, btf_id, bp->ptr, buf, + end - buf_start, flags); + + return widen_string(buf, buf - buf_start, end, spec); +} + /* * Show a '%p' thing. A kernel extension is that the '%p' is followed * by an extra set of alphanumeric characters that are extended format @@ -2206,6 +2288,20 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode, * bpf_trace_printk() where [ku] prefix specifies either kernel (k) * or user (u) memory to probe, and: * s a string, equivalent to "%s" on direct vsnprintf() use + * - 'T[cNx0]' For printing struct btf_ptr * data using BPF Type Format (BTF). + * + * Optional arguments are + * c compact (no indentation/newlines) + * N do not print type and member names + * x do not obfuscate pointers + * u do not copy data to safe buffer prior + * to display + * 0 show 0-valued data + * + * BPF_PTR_TYPE(ptr, type) can be used to place pointer and type string + * in the "struct btf_ptr *" expected; for example: + * + * printk(KERN_INFO "%pT", BTF_PTR_TYPE(skb, struct sk_buff)); * * ** When making changes please also update: * Documentation/core-api/printk-formats.rst @@ -2297,6 +2393,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, default: return error_string(buf, end, "(einval)", spec); } + case 'T': + return btf_string(buf, end, ptr, spec, fmt + 1); } /* default is to _not_ leak addresses, hash before printing */ From patchwork Tue Jun 23 12:07:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 217327 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, UNPARSEABLE_RELAY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A03BDC433E2 for ; Tue, 23 Jun 2020 12:10:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6F94E206A5 for ; Tue, 23 Jun 2020 12:10:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b="mA8wxUvk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732708AbgFWMKh (ORCPT ); Tue, 23 Jun 2020 08:10:37 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:51402 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732665AbgFWMKW (ORCPT ); Tue, 23 Jun 2020 08:10:22 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC7mGO052074; Tue, 23 Jun 2020 12:09:29 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=5KtWagpRkczJBtfBJ6H66/ElH0imVZdlP7CDOBseCAw=; b=mA8wxUvk6JnRSfAjXZoF2EHVamYkxnBQobd93HsoUHe3mfSTecDpOdyCKS8JXtRAiA7/ WUh8Q+RShp//HmIybnHtXdgW0NrGdsFT8c0dH02oWqoQqDDF1dghjcKhIR1tqDm82aem tTBSiF75CqoHFLUqnimpvLNGkaUMS5vSTfxNJUtEa0fjm/kWZjRbLGHbyekCMr6TDkMB SXbiklzqEbfaGEtcb5kN9cPJnNT86VIp9BrP6hTdtp9OwaAIglVdlJa+m1i374TSywKK yhkuDSavLsgYAgTsu54boE8zD5q+O5Wc0aPIQbuJMhEVKjuaLX3IJ6O3Ma0B2YDQnY0O lg== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by aserp2120.oracle.com with ESMTP id 31sebbcvbd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jun 2020 12:09:29 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC7gE6185211; Tue, 23 Jun 2020 12:09:28 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by aserp3030.oracle.com with ESMTP id 31sv1n7h1s-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 23 Jun 2020 12:09:28 +0000 Received: from abhmp0017.oracle.com (abhmp0017.oracle.com [141.146.116.23]) by aserv0122.oracle.com (8.14.4/8.14.4) with ESMTP id 05NC9SZl022676; Tue, 23 Jun 2020 12:09:28 GMT Received: from localhost.uk.oracle.com (/10.175.166.3) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 23 Jun 2020 12:09:27 +0000 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, yhs@fb.com, andriin@fb.com, arnaldo.melo@gmail.com Cc: kafai@fb.com, songliubraving@fb.com, john.fastabend@gmail.com, kpsingh@chromium.org, linux@rasmusvillemoes.dk, joe@perches.com, pmladek@suse.com, rostedt@goodmis.org, sergey.senozhatsky@gmail.com, andriy.shevchenko@linux.intel.com, corbet@lwn.net, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v3 bpf-next 6/8] printk: extend test_printf to test %pT BTF-based format specifier Date: Tue, 23 Jun 2020 13:07:09 +0100 Message-Id: <1592914031-31049-7-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> References: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 mlxlogscore=999 adultscore=0 phishscore=0 bulkscore=0 suspectscore=0 malwarescore=0 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxlogscore=999 cotscore=-2147483648 lowpriorityscore=0 phishscore=0 bulkscore=0 clxscore=1015 impostorscore=0 malwarescore=0 priorityscore=1501 spamscore=0 mlxscore=0 adultscore=0 suspectscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add tests to verify basic type display and to iterate through all enums, structs, unions and typedefs ensuring expected behaviour occurs. Since test_printf can be built as a module we need to export a BTF kind iterator function to allow us to iterate over all names of a particular BTF kind. These changes add up to approximately 20,000 new tests covering all enum, struct, union and typedefs in vmlinux BTF. Individual tests are also added for int, char, struct, enum and typedefs which verify output is as expected. Signed-off-by: Alan Maguire --- include/linux/btf.h | 3 + kernel/bpf/btf.c | 33 ++++++ lib/test_printf.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) diff --git a/include/linux/btf.h b/include/linux/btf.h index e8dbf0c..e3102a7 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -191,4 +191,7 @@ static inline const char *btf_name_by_offset(const struct btf *btf, } #endif +/* Following function used for testing BTF-based printk-family support */ +const char *btf_vmlinux_next_type_name(u8 kind, s32 *id); + #endif diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index c82cb18..4e250cd 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5459,3 +5459,36 @@ u32 btf_id(const struct btf *btf) { return btf->id; } + +/* + * btf_vmlinux_next_type_name(): used in test_printf.c to + * iterate over types for testing. + * Exported as test_printf can be built as a module. + * + * @kind: BTF_KIND_* value + * @id: pointer to last id; value/result argument. When next + * type name is found, we set *id to associated id. + * Returns: + * Next type name, sets *id to associated id. + */ +const char *btf_vmlinux_next_type_name(u8 kind, s32 *id) +{ + const struct btf *btf = bpf_get_btf_vmlinux(); + const struct btf_type *t; + const char *name; + + if (!btf || !id) + return NULL; + + for ((*id)++; *id <= btf->nr_types; (*id)++) { + t = btf->types[*id]; + if (BTF_INFO_KIND(t->info) != kind) + continue; + name = btf_name_by_offset(btf, t->name_off); + if (name && strlen(name) > 0) + return name; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(btf_vmlinux_next_type_name); diff --git a/lib/test_printf.c b/lib/test_printf.c index 7ac87f1..7ce7387 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -23,6 +23,9 @@ #include #include +#include +#include +#include #include "../tools/testing/selftests/kselftest_module.h" @@ -669,6 +672,318 @@ static void __init fwnode_pointer(void) #endif } +#define __TEST_BTF(fmt, type, ptr, expected) \ + test(expected, "%pT"fmt, ptr) + +#define TEST_BTF_C(type, var, ...) \ + do { \ + type var = __VA_ARGS__; \ + struct btf_ptr *ptr = BTF_PTR_TYPE(&var, type); \ + pr_debug("type %s: %pTc", #type, ptr); \ + __TEST_BTF("c", type, ptr, "(" #type ")" #__VA_ARGS__); \ + } while (0) + +#define TEST_BTF(fmt, type, var, expected, ...) \ + do { \ + type var = __VA_ARGS__; \ + struct btf_ptr *ptr = BTF_PTR_TYPE(&var, type); \ + pr_debug("type %s: %pT"fmt, #type, ptr); \ + __TEST_BTF(fmt, type, ptr, expected); \ + } while (0) + +#define BTF_MAX_DATA_SIZE 65536 + +static void __init +btf_print_kind(u8 kind, const char *kind_name, u64 fillval) +{ + const char *fmt1 = "%pT", *fmt2 = "%pTN", *fmt3 = "%pT0"; + const char *name, *fmt = fmt1; + int i, res1, res2, res3, res4; + char type_name[256]; + char *buf, *buf2; + u8 *dummy_data; + s32 id = 0; + + dummy_data = kzalloc(BTF_MAX_DATA_SIZE, GFP_KERNEL); + + /* fill our dummy data with supplied fillval. */ + for (i = 0; i < BTF_MAX_DATA_SIZE; i++) + dummy_data[i] = fillval; + + buf = kzalloc(BTF_MAX_DATA_SIZE, GFP_KERNEL); + buf2 = kzalloc(BTF_MAX_DATA_SIZE, GFP_KERNEL); + + for (;;) { + name = btf_vmlinux_next_type_name(kind, &id); + if (!name) + break; + + total_tests++; + + snprintf(type_name, sizeof(type_name), "%s%s", + kind_name, name); + + res1 = snprintf(buf, BTF_MAX_DATA_SIZE, fmt1, + BTF_PTR_TYPE(dummy_data, type_name)); + res2 = snprintf(buf, 0, fmt1, + BTF_PTR_TYPE(dummy_data, type_name)); + res3 = snprintf(buf, BTF_MAX_DATA_SIZE, fmt2, + BTF_PTR_TYPE(dummy_data, type_name)); + res4 = snprintf(buf, BTF_MAX_DATA_SIZE, fmt3, + BTF_PTR_TYPE(dummy_data, type_name)); + + (void) snprintf(buf, BTF_MAX_DATA_SIZE, "%pT", + BTF_PTR_TYPE(dummy_data, type_name)); + (void) snprintf(buf2, BTF_MAX_DATA_SIZE, "%pT", + BTF_PTR_TYPE(dummy_data, type_name)); + + /* + * Ensure return value is > 0 and identical irrespective + * of whether we pass in a big enough buffer; + * also ensure that printing names always results in as + * long/longer buffer length. + */ + if (res1 <= 0 || res2 <= 0 || res3 <= 0 || res4 <= 0) { + if (res3 <= 0) + fmt = fmt2; + if (res4 <= 0) + fmt = fmt3; + + pr_warn("snprintf(%s%s); %d <= 0 (fmt %s)", + kind_name, name, + res1 <= 0 ? res1 : res2 <= 0 ? res2 : + res3 <= 0 ? res3 : res4, fmt); + failed_tests++; + } else if (res1 != res2) { + pr_warn("snprintf(%s%s): %d (to buf) != %d (no buf)", + kind_name, name, res1, res2); + failed_tests++; + } else if (res3 > res2) { + pr_warn("snprintf(%s%s); %d (no names) > %d (names)", + kind_name, name, res3, res2); + failed_tests++; + } else if (strcmp(buf, buf2) != 0) { + /* Safe and unsafe buffers should match. */ + pr_warn("snprintf(%s%s); safe != unsafe", + kind_name, name); + pr_warn("safe: %s", buf); + pr_warn("unsafe: %s", buf2); + failed_tests++; + } else { + pr_debug("Printed %s%s (%d bytes)", + kind_name, name, res1); + } + } + kfree(dummy_data); + kfree(buf); + kfree(buf2); +} + +/* + * For BTF it is the struct btf_ptr * ptr field, not the pointer itself + * which gets displayed, so it is that we need to hash. + */ +static void __init +test_btf_hashed(const char *fmt, struct btf_ptr *p) +{ + char buf[PLAIN_BUF_SIZE]; + int ret; + + ret = plain_hash_to_buffer(p->ptr, buf, PLAIN_BUF_SIZE); + if (ret) + return; + + test(buf, fmt, p); +} + +#ifdef CONFIG_DEBUG_INFO_BTF + +static void __init btf_pointer_test_int(void) +{ + /* simple int */ + TEST_BTF_C(int, testint, 1234); + TEST_BTF("cN", int, testint, "1234", 1234); + /* zero value should be printed at toplevel */ + TEST_BTF("c", int, testint, "(int)0", 0); + TEST_BTF("cN", int, testint, "0", 0); + TEST_BTF("c0", int, testint, "(int)0", 0); + TEST_BTF("cN0", int, testint, "0", 0); + TEST_BTF_C(int, testint, -4567); + TEST_BTF("cN", int, testint, "-4567", -4567); +} + +static void __init btf_pointer_test_char(void) +{ + /* simple char */ + TEST_BTF_C(char, testchar, 100); + TEST_BTF("cN", char, testchar, "100", 100); + /* zero value should be printed at toplevel */ + TEST_BTF("c", char, testchar, "(char)0", 0); + TEST_BTF("cN", char, testchar, "0", 0); + TEST_BTF("c0", char, testchar, "(char)0", 0); + TEST_BTF("cN0", char, testchar, "0", 0); +} + +static void __init btf_pointer_test_typedef(void) +{ + /* simple typedef */ + TEST_BTF_C(phys_addr_t, testtype, 100); + TEST_BTF("cN", phys_addr_t, testtype, "1", 1); + /* zero value should be printed at toplevel */ + TEST_BTF("c", phys_addr_t, testtype, "(phys_addr_t)0", 0); + TEST_BTF("cN", phys_addr_t, testtype, "0", 0); + TEST_BTF("c0", phys_addr_t, testtype, "(phys_addr_t)0", 0); + TEST_BTF("cN0", phys_addr_t, testtype, "0", 0); + + /* typedef struct */ + TEST_BTF_C(atomic_t, testtype, {.counter = (int)1,}); + TEST_BTF("cN", atomic_t, testtype, "{1,}", {.counter = 1,}); + /* typedef with 0 value should be printed at toplevel */ + TEST_BTF("c", atomic_t, testtype, "(atomic_t){}", {.counter = 0,}); + TEST_BTF("cN", atomic_t, testtype, "{}", {.counter = 0,}); + TEST_BTF("c0", atomic_t, testtype, "(atomic_t){.counter = (int)0,}", + {.counter = 0,}); + TEST_BTF("cN0", atomic_t, testtype, "{0,}", {.counter = 0,}); + + /* typedef array */ + TEST_BTF("c", cpumask_t, testtype, + "(cpumask_t){.bits = (long unsigned int[])[1,],}", + { .bits = {1,}}); + TEST_BTF("cN", cpumask_t, testtype, "{[1,],}", + { .bits = {1,}}); + /* typedef with 0 value should be printed at toplevel */ + TEST_BTF("c", cpumask_t, testtype, "(cpumask_t){}", {{ 0 }}); +} + +static void __init btf_pointer_test_enum(void) +{ + /* enum where enum value does (and does not) exist */ + TEST_BTF_C(enum bpf_cmd, testenum, BPF_MAP_CREATE); + TEST_BTF("c", enum bpf_cmd, testenum, "(enum bpf_cmd)BPF_MAP_CREATE", + 0); + TEST_BTF("cN", enum bpf_cmd, testenum, "BPF_MAP_CREATE", + BPF_MAP_CREATE); + TEST_BTF("cN0", enum bpf_cmd, testenum, "BPF_MAP_CREATE", 0); + + TEST_BTF("c0", enum bpf_cmd, testenum, "(enum bpf_cmd)BPF_MAP_CREATE", + BPF_MAP_CREATE); + TEST_BTF("cN0", enum bpf_cmd, testenum, "BPF_MAP_CREATE", + BPF_MAP_CREATE); + TEST_BTF_C(enum bpf_cmd, testenum, 2000); + TEST_BTF("cN", enum bpf_cmd, testenum, "2000", 2000); +} + +static void __init btf_pointer_test_struct(void) +{ + /* simple struct */ + TEST_BTF_C(struct btf_enum, teststruct, + {.name_off = (__u32)3,.val = (__s32)-1,}); + TEST_BTF("cN", struct btf_enum, teststruct, "{3,-1,}", + { .name_off = 3, .val = -1,}); + TEST_BTF("cN", struct btf_enum, teststruct, "{-1,}", + { .name_off = 0, .val = -1,}); + TEST_BTF("cN0", struct btf_enum, teststruct, "{0,-1,}", + { .name_off = 0, .val = -1,}); + /* empty struct should be printed */ + TEST_BTF("c", struct btf_enum, teststruct, "(struct btf_enum){}", + { .name_off = 0, .val = 0,}); + TEST_BTF("cN", struct btf_enum, teststruct, "{}", + { .name_off = 0, .val = 0,}); + TEST_BTF("c0", struct btf_enum, teststruct, + "(struct btf_enum){.name_off = (__u32)0,.val = (__s32)0,}", + { .name_off = 0, .val = 0,}); + + /* struct with pointers */ + TEST_BTF("cx", struct skb_shared_info, testptr, + "(struct skb_shared_info){.frag_list = (struct sk_buff *)0x0000000000000001,}", + { .frag_list = (struct sk_buff *)1 }); + /* NULL pointer should not be displayed */ + TEST_BTF("cx", struct skb_shared_info, testptr, + "(struct skb_shared_info){}", + { .frag_list = (struct sk_buff *)0 }); + + /* struct with char array */ + TEST_BTF("c", struct bpf_prog_info, teststruct, + "(struct bpf_prog_info){.name = (char[])['f','o','o',],}", + { .name = "foo",}); + TEST_BTF("cN", struct bpf_prog_info, teststruct, + "{['f','o','o',],}", + {.name = "foo",}); + /* leading null char means do not display string */ + TEST_BTF("c", struct bpf_prog_info, teststruct, + "(struct bpf_prog_info){}", + {.name = {'\0', 'f', 'o', 'o'}}); + /* handle non-printable characters */ + TEST_BTF("c", struct bpf_prog_info, teststruct, + "(struct bpf_prog_info){.name = (char[])[1,2,3,],}", + { .name = {1, 2, 3, 0}}); + + /* struct with non-char array */ + TEST_BTF("c", struct __sk_buff, teststruct, + "(struct __sk_buff){.cb = (__u32[])[1,2,3,4,5,],}", + { .cb = {1, 2, 3, 4, 5,},}); + TEST_BTF("cN", struct __sk_buff, teststruct, + "{[1,2,3,4,5,],}", + { .cb = { 1, 2, 3, 4, 5},}); + /* For non-char, arrays, show non-zero values only */ + TEST_BTF("c", struct __sk_buff, teststruct, + "(struct __sk_buff){.cb = (__u32[])[1,],}", + { .cb = { 0, 0, 1, 0, 0},}); + + /* struct with struct array */ + TEST_BTF("c", struct bpf_struct_ops, teststruct, + "(struct bpf_struct_ops){.func_models = (struct btf_func_model[])[(struct btf_func_model){.ret_size = (u8)1,.nr_args = (u8)2,.arg_size = (u8[])[3,4,5,],},],}", + { .func_models = {{ .ret_size = 1, .nr_args = 2, + .arg_size = { 3, 4, 5,},}}}); + + /* struct with bitfields */ + TEST_BTF_C(struct bpf_insn, testbitfield, + {.code = (__u8)1,.dst_reg = (__u8)0x2,.src_reg = (__u8)0x3,.off = (__s16)4,.imm = (__s32)5,}); + TEST_BTF("cN", struct bpf_insn, testbitfield, "{1,0x2,0x3,4,5,}", + {.code = 1, .dst_reg = 0x2, .src_reg = 0x3, .off = 4, + .imm = 5,}); + + /* struct with anon struct/unions */ + TEST_BTF("cx", struct sk_buff, test_anon, + "(struct sk_buff){(union){(struct){(union){.dev = (struct net_device *)0x0000000000000001,.dev_scratch = (long unsigned int)1,},},.rbnode = (struct rb_node){.rb_left = (struct rb_node *)0x0000000000000001,},},}", + { .dev_scratch = 1 }); +} + +#endif /* CONFIG_DEBUG_INFO_BTF */ + +static void __init +btf_pointer(void) +{ + struct sk_buff *skb = alloc_skb(64, GFP_KERNEL); + u64 fillvals[] = { 0x0, 0xffffffffffffffff, 0x0123456789abcdef }; + int i; + +#ifdef CONFIG_DEBUG_INFO_BTF + btf_pointer_test_int(); + btf_pointer_test_char(); + btf_pointer_test_typedef(); + btf_pointer_test_enum(); + btf_pointer_test_struct(); +#endif /* CONFIG_DEBUG_INFO_BTF */ + + /* + * Iterate every instance of each kind, printing each associated type. + * This constitutes around 10k tests. + */ + for (i = 0; i < ARRAY_SIZE(fillvals); i++) { + btf_print_kind(BTF_KIND_STRUCT, "struct ", fillvals[i]); + btf_print_kind(BTF_KIND_UNION, "union ", fillvals[i]); + btf_print_kind(BTF_KIND_ENUM, "enum ", fillvals[i]); + btf_print_kind(BTF_KIND_TYPEDEF, "", fillvals[i]); + } + + /* verify unknown type falls back to hashed pointer display */ + test("(null)", "%pT", BTF_PTR_TYPE(NULL, "unknown_type")); + test_btf_hashed("%pT", BTF_PTR_TYPE(skb, "unknown_type")); + + kfree_skb(skb); +} + static void __init test_pointer(void) { @@ -694,6 +1009,7 @@ static void __init fwnode_pointer(void) flags(); errptr(); fwnode_pointer(); + btf_pointer(); } static void __init selftest(void) From patchwork Tue Jun 23 12:07:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Maguire X-Patchwork-Id: 217328 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.1 required=3.0 tests=DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, UNPARSEABLE_RELAY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CFC99C433DF for ; Tue, 23 Jun 2020 12:10:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A5701206A5 for ; Tue, 23 Jun 2020 12:10:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=oracle.com header.i=@oracle.com header.b="a+pyfdhE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732692AbgFWMK1 (ORCPT ); Tue, 23 Jun 2020 08:10:27 -0400 Received: from aserp2120.oracle.com ([141.146.126.78]:51434 "EHLO aserp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732673AbgFWMKW (ORCPT ); Tue, 23 Jun 2020 08:10:22 -0400 Received: from pps.filterd (aserp2120.oracle.com [127.0.0.1]) by aserp2120.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC7xCh052195; Tue, 23 Jun 2020 12:09:35 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2020-01-29; bh=E8fHB7DcBFvLhd8t1qiFwiqXsKebHCqIA8SKLWeX8Ag=; b=a+pyfdhEhLKRep9FR7qYoiPUEWy0kACfD8ai7BBZmObjop4905J8HClw/pRdHewtPbWX 4SQ0oit24pjlCFLzDp0D98+1l123gFbnLnlRglLjiPRDyVHAj5TENfs4jrtGQE92o61g 17vsBoitadRNLEHx/hwMQ1AHQjJ2WICZHIAFNRFgqMhnjdNyNI/36XeLW4WNQ9szd588 9sas0/GTfq0Y6UwSObU4HT/SgZDrwUqlS0W5SgL7Nj2oduxCDptYT3OndNOlbO8qdaY3 DF27tEuV6haRr0Qx+rZzfmVSM67ssLzFM9Jrhq9nV5R2DD+YOud/25DIpLlEBgXaaSYK Ew== Received: from aserp3030.oracle.com (aserp3030.oracle.com [141.146.126.71]) by aserp2120.oracle.com with ESMTP id 31sebbcvbx-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jun 2020 12:09:35 +0000 Received: from pps.filterd (aserp3030.oracle.com [127.0.0.1]) by aserp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 05NC7kwY185627; Tue, 23 Jun 2020 12:09:35 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by aserp3030.oracle.com with ESMTP id 31sv1n7h3r-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 23 Jun 2020 12:09:34 +0000 Received: from abhmp0017.oracle.com (abhmp0017.oracle.com [141.146.116.23]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 05NC9W1v025702; Tue, 23 Jun 2020 12:09:32 GMT Received: from localhost.uk.oracle.com (/10.175.166.3) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 23 Jun 2020 12:09:32 +0000 From: Alan Maguire To: ast@kernel.org, daniel@iogearbox.net, yhs@fb.com, andriin@fb.com, arnaldo.melo@gmail.com Cc: kafai@fb.com, songliubraving@fb.com, john.fastabend@gmail.com, kpsingh@chromium.org, linux@rasmusvillemoes.dk, joe@perches.com, pmladek@suse.com, rostedt@goodmis.org, sergey.senozhatsky@gmail.com, andriy.shevchenko@linux.intel.com, corbet@lwn.net, bpf@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v3 bpf-next 7/8] bpf: add support for %pT format specifier for bpf_trace_printk() helper Date: Tue, 23 Jun 2020 13:07:10 +0100 Message-Id: <1592914031-31049-8-git-send-email-alan.maguire@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> References: <1592914031-31049-1-git-send-email-alan.maguire@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 mlxlogscore=999 adultscore=0 phishscore=0 bulkscore=0 suspectscore=0 malwarescore=0 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9660 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxlogscore=999 cotscore=-2147483648 lowpriorityscore=0 phishscore=0 bulkscore=0 clxscore=1015 impostorscore=0 malwarescore=0 priorityscore=1501 spamscore=0 mlxscore=0 adultscore=0 suspectscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006230097 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Allow %pT[cNx0] format specifier for BTF-based display of data associated with pointer. The unsafe data modifier 'u' - where the source data is traversed without copying it to a safe buffer via probe_kernel_read() - is not supported. Signed-off-by: Alan Maguire --- include/uapi/linux/bpf.h | 27 ++++++++++++++++++++++----- kernel/trace/bpf_trace.c | 24 +++++++++++++++++++++++- tools/include/uapi/linux/bpf.h | 27 ++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1968481..ea4fbf3 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -702,7 +702,12 @@ struct bpf_stack_build_id { * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is - * limited to five). + * limited to five), and also supports %pT (BTF-based type + * printing), as long as BPF_READ lockdown is not active. + * "%pT" takes a "struct __btf_ptr *" as an argument; it + * consists of a pointer value and specified BTF type string or id + * used to select the type for display. For more details, see + * Documentation/core-api/printk-formats.rst. * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is @@ -738,10 +743,10 @@ struct bpf_stack_build_id { * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, - * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size - * of field, padding with zeroes, etc.) is available, and the - * helper will return **-EINVAL** (but print nothing) if it - * encounters an unknown specifier. + * **%lli**, **%llu**, **%llx**, **%p**, **%pT[cNx0], **%s**. + * Only %pT supports modifiers, and the helper will return + * **-EINVAL** (but print nothing) if it encouters an unknown + * specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice @@ -4260,4 +4265,16 @@ struct bpf_pidns_info { __u32 pid; __u32 tgid; }; + +/* + * struct __btf_ptr is used for %pT (typed pointer) display; the + * additional type string/BTF id are used to render the pointer + * data as the appropriate type. + */ +struct __btf_ptr { + void *ptr; + const char *type; + __u32 id; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index e729c9e5..33ddb31 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -374,9 +374,13 @@ static void bpf_trace_copy_string(char *buf, void *unsafe_ptr, char fmt_ptype, } } +/* Unsafe BTF display ('u' modifier) is absent here. */ +#define is_btf_safe_modifier(c) \ + (c == 'c' || c == 'N' || c == 'x' || c == '0') + /* * Only limited trace_printk() conversion specifiers allowed: - * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %pks %pus %s + * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %pks %pus %s %pT */ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1, u64, arg2, u64, arg3) @@ -412,6 +416,24 @@ static void bpf_trace_copy_string(char *buf, void *unsafe_ptr, char fmt_ptype, i++; } else if (fmt[i] == 'p') { mod[fmt_cnt]++; + + /* + * allow BTF type-based printing, but disallow unsafe + * mode - this ensures the data is copied safely + * using probe_kernel_read() prior to traversing it. + */ + if (fmt[i + 1] == 'T') { + int ret; + + ret = security_locked_down(LOCKDOWN_BPF_READ); + if (unlikely(ret < 0)) + return ret; + i += 2; + while (is_btf_safe_modifier(fmt[i])) + i++; + goto fmt_next; + } + if ((fmt[i + 1] == 'k' || fmt[i + 1] == 'u') && fmt[i + 2] == 's') { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 1968481..ea4fbf3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -702,7 +702,12 @@ struct bpf_stack_build_id { * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if * available. It can take up to three additional **u64** * arguments (as an eBPF helpers, the total number of arguments is - * limited to five). + * limited to five), and also supports %pT (BTF-based type + * printing), as long as BPF_READ lockdown is not active. + * "%pT" takes a "struct __btf_ptr *" as an argument; it + * consists of a pointer value and specified BTF type string or id + * used to select the type for display. For more details, see + * Documentation/core-api/printk-formats.rst. * * Each time the helper is called, it appends a line to the trace. * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is @@ -738,10 +743,10 @@ struct bpf_stack_build_id { * The conversion specifiers supported by *fmt* are similar, but * more limited than for printk(). They are **%d**, **%i**, * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, - * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size - * of field, padding with zeroes, etc.) is available, and the - * helper will return **-EINVAL** (but print nothing) if it - * encounters an unknown specifier. + * **%lli**, **%llu**, **%llx**, **%p**, **%pT[cNx0], **%s**. + * Only %pT supports modifiers, and the helper will return + * **-EINVAL** (but print nothing) if it encouters an unknown + * specifier. * * Also, note that **bpf_trace_printk**\ () is slow, and should * only be used for debugging purposes. For this reason, a notice @@ -4260,4 +4265,16 @@ struct bpf_pidns_info { __u32 pid; __u32 tgid; }; + +/* + * struct __btf_ptr is used for %pT (typed pointer) display; the + * additional type string/BTF id are used to render the pointer + * data as the appropriate type. + */ +struct __btf_ptr { + void *ptr; + const char *type; + __u32 id; +}; + #endif /* _UAPI__LINUX_BPF_H__ */