Received: by 2002:ac0:8845:0:0:0:0:0 with SMTP id g63csp801153img; Thu, 28 Feb 2019 08:05:56 -0800 (PST) X-Google-Smtp-Source: APXvYqzbSoDWHRxl5qUKIHWO/ggVCpyGupTyO9daXVbnX6ka0MeddEIUM26Kklxql/sBs3yNwlDx X-Received: by 2002:a17:902:6686:: with SMTP id e6mr117832plk.208.1551369956231; Thu, 28 Feb 2019 08:05:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1551369956; cv=none; d=google.com; s=arc-20160816; b=QWBDZBwU5hWMSWiMqAMVUIRzOTMO4uJExjXZpKpI3Arb0qd4qsyCX9ft9illojOfvt sDferLE+rU1uwecTcAw09vhqO1Eluu8boz4X2MhJcnlGq3D1cB5ruFddQvJ+6Nnxrz8o J+CAcVn22B2NHGHaXc1yETjjxipVq8CpE574MRIs780ORFFLGyrL+Kh9+EZjuqqKrMKH 9jezF9KAAtihIW9ebPEMp7fvDIub6zCmOgPY8jqPsgrREGVXI3qNr3LIZmVM7a/0u+9h zDUSLqsnPihapQyqwof7FBSSFUgmMtmAmvp3QWoBqaSTHulNuzQtOTUmiNYIZpzvjbMu wsMQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=eCgukpkRk6LUM+hlDBX5GREc9TdmyXEaJreshGUdy3k=; b=FcM/QDva7iCzXdrDW3lPzhXxhOJqKG/QR1XfQIu2/VbOzIhAcDZLkrg/8gH4ijKeyM cJVNQieStZZQtgVcVRe3RNfnkHrzdMxseSzWq6Bo1cSEunYQaU54d8oOSgnG8oh/bz3E g80WR1rLjUGVFoaENu2qpmlUNKIdjTkV33r8wnlMN52fslt2s9ddu6ZRShw0Pmr9x+Ux mghgX5ZfIJ650PBwbUAlW1c1U6eySR9FvAhAjf+tcxWc89d4uGyFu42uXSY56+S9J4Ey tFwdRvgokr89wKAmRNnAUGWpdLQ3+Pwb3ygxc0vZsgASBxZOYOPVsu4dB/jkvZzY1emG 99TQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=uvVHZZpm; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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. [209.132.180.67]) by mx.google.com with ESMTP id a89si18541435pla.362.2019.02.28.08.05.39; Thu, 28 Feb 2019 08:05:56 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=uvVHZZpm; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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 S1732836AbfB1QEu (ORCPT + 99 others); Thu, 28 Feb 2019 11:04:50 -0500 Received: from mail.kernel.org ([198.145.29.99]:50456 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731142AbfB1QEu (ORCPT ); Thu, 28 Feb 2019 11:04:50 -0500 Received: from localhost.localdomain (NE2965lan1.rev.em-net.ne.jp [210.141.244.193]) (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 5476A2184A; Thu, 28 Feb 2019 16:04:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1551369888; bh=wf75M2+jt92a5Cm2tC+0Lh9jKYfMxYtyy4r69mXqruM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uvVHZZpmNSPfKMTeOQ47e8ISKBhl8FLF++LkT1vd5goa6CZ6ErKHLd50HdgWgQvf2 XlIQ0CTaVZLdD77fNg1QK+VRTGqLYgRHPA+hBc38uw6wj97ECuK0SdIy75TGeROmtr OOJT6Hiqr6KB4gyUygFge/Mo3W68h9tFEZG5ULgw= From: Masami Hiramatsu To: Steven Rostedt , Linus Torvalds , Shuah Khan Cc: mhiramat@kernel.org, linux-kernel@vger.kernel.org, Andy Lutomirski , Ingo Molnar , Andrew Morton , Changbin Du , Jann Horn , Kees Cook , Andy Lutomirski , Alexei Starovoitov , Nadav Amit , Peter Zijlstra , Joel Fernandes , yhs@fb.com Subject: [PATCH v5 4/6] tracing/probe: Add ustring type for user-space string Date: Fri, 1 Mar 2019 01:04:24 +0900 Message-Id: <155136986438.2968.16955432478220148944.stgit@devbox> X-Mailer: git-send-email 2.13.6 In-Reply-To: <155136974478.2968.3105123100519786079.stgit@devbox> References: <155136974478.2968.3105123100519786079.stgit@devbox> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add "ustring" type for fetching user-space string from kprobe event. User can specify ustring type at uprobe event, and it is same as "string" for uprobe. Note that probe-event provides this option but it doesn't choose the correct type automatically since we have not way to decide the address is in user-space or not on some arch (and on some other arch, you can fetch the string by "string" type). So user must carefully check the target code (e.g. if you see __user on the target variable) and use this new type. Signed-off-by: Masami Hiramatsu Acked-by: Steven Rostedt (VMware) --- Changes in v5: - Use strnlen_unsafe_user() in fetch_store_strlen_user(). Changes in v2: - Use strnlen_user() instead of open code for fetch_store_strlen_user(). --- Documentation/trace/kprobetrace.rst | 9 +++++++-- kernel/trace/trace.c | 2 +- kernel/trace/trace_kprobe.c | 29 +++++++++++++++++++++++++++++ kernel/trace/trace_probe.c | 14 +++++++++++--- kernel/trace/trace_probe.h | 1 + kernel/trace/trace_probe_tmpl.h | 14 +++++++++++++- kernel/trace/trace_uprobe.c | 12 ++++++++++++ 7 files changed, 74 insertions(+), 7 deletions(-) diff --git a/Documentation/trace/kprobetrace.rst b/Documentation/trace/kprobetrace.rst index 235ce2ab131a..a3ac7c9ac242 100644 --- a/Documentation/trace/kprobetrace.rst +++ b/Documentation/trace/kprobetrace.rst @@ -55,7 +55,8 @@ Synopsis of kprobe_events NAME=FETCHARG : Set NAME as the argument name of FETCHARG. FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types - (x8/x16/x32/x64), "string" and bitfield are supported. + (x8/x16/x32/x64), "string", "ustring" and bitfield + are supported. (\*1) only for the probe on function entry (offs == 0). (\*2) only for return probe. @@ -77,7 +78,11 @@ apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is wrong, but '+8($stack):x8[8]' is OK.) String type is a special type, which fetches a "null-terminated" string from kernel space. This means it will fail and store NULL if the string container -has been paged out. +has been paged out. "ustring" type is an alternative of string for user-space. +Note that kprobe-event provides string/ustring types, but doesn't change it +automatically. So user has to decide if the targe string in kernel or in user +space carefully. On some arch, if you choose wrong one, it always fails to +record string data. The string array type is a bit different from other types. For other base types, [1] is equal to (e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not equal to string. The string type itself diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c4238b441624..4cacbb0e1538 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4643,7 +4643,7 @@ static const char readme_msg[] = "\t $stack, $stack, $retval, $comm\n" #endif "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" - "\t b@/,\n" + "\t b@/, ustring,\n" "\t \\[\\]\n" #ifdef CONFIG_HIST_TRIGGERS "\t field: ;\n" diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 9eaf07f99212..43b5c3230773 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -872,6 +872,14 @@ fetch_store_strlen(unsigned long addr) return (ret < 0) ? ret : len; } +/* Return the length of string -- including null terminal byte */ +static nokprobe_inline int +fetch_store_strlen_user(unsigned long addr) +{ + return strnlen_unsafe_user((__force const void __user *)addr, + MAX_STRING_SIZE); +} + /* * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max * length and relative data location. @@ -896,6 +904,27 @@ fetch_store_string(unsigned long addr, void *dest, void *base) return ret; } +/* + * Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf + * with max length and relative data location. + */ +static nokprobe_inline int +fetch_store_string_user(unsigned long addr, void *dest, void *base) +{ + int maxlen = get_loc_len(*(u32 *)dest); + u8 *dst = get_loc_data(dest, base); + long ret; + + if (unlikely(!maxlen)) + return -ENOMEM; + ret = strncpy_from_unsafe_user(dst, (__force const void __user *)addr, + maxlen); + + if (ret >= 0) + *(u32 *)dest = make_data_loc(ret, (void *)dst - base); + return ret; +} + static nokprobe_inline int probe_mem_read(void *dest, void *src, size_t size) { diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 9962cb5da8ac..a7012de37a00 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -73,6 +73,8 @@ static const struct fetch_type probe_fetch_types[] = { /* Special types */ __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, "__data_loc char[]"), + __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, + "__data_loc char[]"), /* Basic types */ ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u16, u16, 0), @@ -440,7 +442,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, goto fail; /* Store operation */ - if (!strcmp(parg->type->name, "string")) { + if (!strcmp(parg->type->name, "string") || + !strcmp(parg->type->name, "ustring")) { if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM) { pr_info("string only accepts memory or address.\n"); @@ -459,7 +462,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, goto fail; } } - code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */ + /* If op == DEREF, replace it with STRING */ + if (!strcmp(parg->type->name, "ustring")) + code->op = FETCH_OP_ST_USTRING; + else + code->op = FETCH_OP_ST_STRING; code->size = parg->type->size; parg->dynamic = true; } else if (code->op == FETCH_OP_DEREF) { @@ -484,7 +491,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, /* Loop(Array) operation */ if (parg->count) { if (scode->op != FETCH_OP_ST_MEM && - scode->op != FETCH_OP_ST_STRING) { + scode->op != FETCH_OP_ST_STRING && + scode->op != FETCH_OP_ST_USTRING) { pr_info("array only accepts memory or address\n"); ret = -EINVAL; goto fail; diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 8a63f8bc01bc..cf4ba8bbb841 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -95,6 +95,7 @@ enum fetch_op { FETCH_OP_ST_RAW, /* Raw: .size */ FETCH_OP_ST_MEM, /* Mem: .offset, .size */ FETCH_OP_ST_STRING, /* String: .offset, .size */ + FETCH_OP_ST_USTRING, /* User String: .offset, .size */ // Stage 4 (modify) op FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ // Stage 5 (loop) op diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h index 4737bb8c07a3..7526f6f8d7b0 100644 --- a/kernel/trace/trace_probe_tmpl.h +++ b/kernel/trace/trace_probe_tmpl.h @@ -59,6 +59,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, static nokprobe_inline int fetch_store_strlen(unsigned long addr); static nokprobe_inline int fetch_store_string(unsigned long addr, void *dest, void *base); +static nokprobe_inline int fetch_store_strlen_user(unsigned long addr); +static nokprobe_inline int +fetch_store_string_user(unsigned long addr, void *dest, void *base); static nokprobe_inline int probe_mem_read(void *dest, void *src, size_t size); @@ -91,6 +94,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ret += fetch_store_strlen(val + code->offset); code++; goto array; + } else if (code->op == FETCH_OP_ST_USTRING) { + ret += fetch_store_strlen_user(val + code->offset); + code++; + goto array; } else return -EILSEQ; } @@ -106,6 +113,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, loc = *(u32 *)dest; ret = fetch_store_string(val + code->offset, dest, base); break; + case FETCH_OP_ST_USTRING: + loc = *(u32 *)dest; + ret = fetch_store_string_user(val + code->offset, dest, base); + break; default: return -EILSEQ; } @@ -123,7 +134,8 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, total += ret; if (++i < code->param) { code = s3; - if (s3->op != FETCH_OP_ST_STRING) { + if (s3->op != FETCH_OP_ST_STRING && + s3->op != FETCH_OP_ST_USTRING) { dest += s3->size; val += s3->size; goto stage3; diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 9bde07c06362..92facae8c3d8 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -173,6 +173,12 @@ fetch_store_string(unsigned long addr, void *dest, void *base) return ret; } +static nokprobe_inline int +fetch_store_string_user(unsigned long addr, void *dest, void *base) +{ + return fetch_store_string(addr, dest, base); +} + /* Return the length of string -- including null terminal byte */ static nokprobe_inline int fetch_store_strlen(unsigned long addr) @@ -185,6 +191,12 @@ fetch_store_strlen(unsigned long addr) return (len > MAX_STRING_SIZE) ? 0 : len; } +static nokprobe_inline int +fetch_store_strlen_user(unsigned long addr) +{ + return fetch_store_strlen(addr); +} + static unsigned long translate_user_vaddr(unsigned long file_offset) { unsigned long base_addr;