Received: by 2002:ac0:b08d:0:0:0:0:0 with SMTP id l13csp4503055imc; Mon, 25 Feb 2019 06:08:55 -0800 (PST) X-Google-Smtp-Source: AHgI3IaH/jO15c27pfQTgRmWT1i6sw4T5tNKdRuqDQ2J8oeApigPQH809Vag5ppQi57aF1sCBH5z X-Received: by 2002:a63:2141:: with SMTP id s1mr18785860pgm.363.1551103734987; Mon, 25 Feb 2019 06:08:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1551103734; cv=none; d=google.com; s=arc-20160816; b=uA7PE9gLymSVzLW8vf0+XLP1KfHsvsEy9C0wWnF7PsU2vv/jPDcdVyrfwesb7HtZ8U w5XUoT3bqWnOiz3d3xfgysMvI6kvG1B9+Ld3os4hfdxSMpIpWDV8F6geKZa34mEjwSDz ymb6XoBUM3blI5DeHYqdYfU7dDczRtt4aFlx4tBTht2qc+4KhZTHtLBh+SRxY/1RnszR dFrWsAdv5+kMy0LuZmOgEo3TdUGhPuUJpaBnRLLbQClFCq8NOKoOZCO22hrlHgPL5JWh QujTxfKT+19E/D1c2ZROi2mRcHkK/kGxoHzjX2C6OCRc8MHV2zd+ebRGyjhEu/QD+Unm 4UIg== 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=vWqPxDtjqeieEAJyNXszuw5hKicC2UZpvJ0HJyQVj+E=; b=lwcrqJdHILc9+oQZd+QyAufmO0ZZPALAxGdGN9nzB29sCMA2lwpqAoTXmafkAzfrmB JtTJ8yRRkeUFmY00igVLrxivlnmlVF62brC90rxcGNX61mS816sm0Nxv0AVFYoegBIKF 0srAb1jpOnbCjk90/Y5yanGt/w33L5v1GXFwJcaySlljE2Ka3KarBHiaxuVfi/0NbMof RNc8mQBGO3IARiBS+aF3o9wG+kT2hzRnVRkwZ2SjOAUlZor2jkxavCoEiN3SdCcaG7aj N2QyZn67iKF2NAd2fLU/qydQaf9wtekXzLIKK0WiHOGes123dS6b6Nmlf6QQTs/qAwFO hzIA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=PXRtK0af; 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 l187si9750217pge.71.2019.02.25.06.08.39; Mon, 25 Feb 2019 06:08:54 -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=PXRtK0af; 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 S1727224AbfBYOGf (ORCPT + 99 others); Mon, 25 Feb 2019 09:06:35 -0500 Received: from mail.kernel.org ([198.145.29.99]:51880 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726233AbfBYOGf (ORCPT ); Mon, 25 Feb 2019 09:06:35 -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 4253220663; Mon, 25 Feb 2019 14:06:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1551103594; bh=NcnOdvcwK2ThQcCCreps7EQZEQXBVI0NcrxusHrDtOQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PXRtK0afbXmVoFT4GsowTp6uEHCI3nS9a7/u1yHdfDD+S40aqSFaZSNGu5dbHyWQT zcOgbSf8WJwx/6tGtsAgaHH60m/PqW0i6yVRC3AvctUPcKbxmtQvUMSyO5htcyy7+3 qerbGp2I+Y62Qu9PVtIEVCGPz664CDn9gImAQJBE= From: Masami Hiramatsu To: Steven Rostedt , Linus Torvalds 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 Subject: [RFC PATCH 3/4] tracing/probe: Add ustring type for user-space string Date: Mon, 25 Feb 2019 23:06:10 +0900 Message-Id: <155110357025.21156.7059528437932434521.stgit@devbox> X-Mailer: git-send-email 2.13.6 In-Reply-To: <155110348217.21156.3874419272673328527.stgit@devbox> References: <155110348217.21156.3874419272673328527.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 --- Documentation/trace/kprobetrace.rst | 9 ++++- kernel/trace/trace.c | 2 + kernel/trace/trace_kprobe.c | 59 +++++++++++++++++++++++++++++++++++ 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, 104 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..d50d937b6933 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -872,6 +872,44 @@ fetch_store_strlen(unsigned long addr) return (ret < 0) ? ret : len; } +static nokprobe_inline int __fetch_strlen_user(const char __user *addr) +{ + int ret, len = 0; + u8 c; + + if (!access_ok(addr, MAX_STRING_SIZE)) + return -EFAULT; + + pagefault_disable(); + + do { + ret = __get_user(c, addr + len); + len++; + } while (c && ret == 0 && len < MAX_STRING_SIZE); + + pagefault_enable(); + + 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) +{ + mm_segment_t old_fs = get_fs(); + int ret; + + if (segment_eq(old_fs, USER_DS)) { + ret = __fetch_strlen_user((__force const char __user *)addr); + } else { + set_fs(USER_DS); + ret = __fetch_strlen_user((__force const char __user *)addr); + set_fs(old_fs); + } + + return ret; +} + /* * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max * length and relative data location. @@ -896,6 +934,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;