Commit 88903c46 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt (VMware)

tracing/probe: Add ustring type for user-space string

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.

Link: http://lkml.kernel.org/r/155789871009.26965.14167558859557329331.stgit@devnote2Acked-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 3d708182
...@@ -55,7 +55,8 @@ Synopsis of kprobe_events ...@@ -55,7 +55,8 @@ Synopsis of kprobe_events
NAME=FETCHARG : Set NAME as the argument name of FETCHARG. NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal 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). (\*1) only for the probe on function entry (offs == 0).
(\*2) only for return probe. (\*2) only for return probe.
...@@ -77,7 +78,11 @@ apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is ...@@ -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.) wrong, but '+8($stack):x8[8]' is OK.)
String type is a special type, which fetches a "null-terminated" string from 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 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 The string array type is a bit different from other types. For other base
types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same
as +0(%di):x32.) But string[1] is not equal to string. The string type itself as +0(%di):x32.) But string[1] is not equal to string. The string type itself
......
...@@ -4847,7 +4847,7 @@ static const char readme_msg[] = ...@@ -4847,7 +4847,7 @@ static const char readme_msg[] =
"\t $stack<index>, $stack, $retval, $comm\n" "\t $stack<index>, $stack, $retval, $comm\n"
#endif #endif
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>,\n" "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
"\t <type>\\[<array-size>\\]\n" "\t <type>\\[<array-size>\\]\n"
#ifdef CONFIG_HIST_TRIGGERS #ifdef CONFIG_HIST_TRIGGERS
"\t field: <stype> <name>;\n" "\t field: <stype> <name>;\n"
......
...@@ -886,6 +886,15 @@ fetch_store_strlen(unsigned long addr) ...@@ -886,6 +886,15 @@ fetch_store_strlen(unsigned long addr)
return (ret < 0) ? ret : len; 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)
{
const void __user *uaddr = (__force const void __user *)addr;
return strnlen_unsafe_user(uaddr, MAX_STRING_SIZE);
}
/* /*
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
* length and relative data location. * length and relative data location.
...@@ -894,19 +903,46 @@ static nokprobe_inline int ...@@ -894,19 +903,46 @@ static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base) fetch_store_string(unsigned long addr, void *dest, void *base)
{ {
int maxlen = get_loc_len(*(u32 *)dest); int maxlen = get_loc_len(*(u32 *)dest);
u8 *dst = get_loc_data(dest, base); void *__dest;
long ret; long ret;
if (unlikely(!maxlen)) if (unlikely(!maxlen))
return -ENOMEM; return -ENOMEM;
__dest = get_loc_data(dest, base);
/* /*
* Try to get string again, since the string can be changed while * Try to get string again, since the string can be changed while
* probing. * probing.
*/ */
ret = strncpy_from_unsafe(dst, (void *)addr, maxlen); ret = strncpy_from_unsafe(__dest, (void *)addr, maxlen);
if (ret >= 0)
*(u32 *)dest = make_data_loc(ret, __dest - 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)
{
const void __user *uaddr = (__force const void __user *)addr;
int maxlen = get_loc_len(*(u32 *)dest);
void *__dest;
long ret;
if (unlikely(!maxlen))
return -ENOMEM;
__dest = get_loc_data(dest, base);
ret = strncpy_from_unsafe_user(__dest, uaddr, maxlen);
if (ret >= 0) if (ret >= 0)
*(u32 *)dest = make_data_loc(ret, (void *)dst - base); *(u32 *)dest = make_data_loc(ret, __dest - base);
return ret; return ret;
} }
......
...@@ -78,6 +78,8 @@ static const struct fetch_type probe_fetch_types[] = { ...@@ -78,6 +78,8 @@ static const struct fetch_type probe_fetch_types[] = {
/* Special types */ /* Special types */
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1,
"__data_loc char[]"), "__data_loc char[]"),
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1,
"__data_loc char[]"),
/* Basic types */ /* Basic types */
ASSIGN_FETCH_TYPE(u8, u8, 0), ASSIGN_FETCH_TYPE(u8, u8, 0),
ASSIGN_FETCH_TYPE(u16, u16, 0), ASSIGN_FETCH_TYPE(u16, u16, 0),
...@@ -569,7 +571,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -569,7 +571,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
goto fail; goto fail;
/* Store operation */ /* 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 && if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM &&
code->op != FETCH_OP_COMM) { code->op != FETCH_OP_COMM) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), trace_probe_log_err(offset + (t ? (t - arg) : 0),
...@@ -590,7 +593,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -590,7 +593,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
goto fail; 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; code->size = parg->type->size;
parg->dynamic = true; parg->dynamic = true;
} else if (code->op == FETCH_OP_DEREF) { } else if (code->op == FETCH_OP_DEREF) {
...@@ -618,7 +625,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -618,7 +625,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
/* Loop(Array) operation */ /* Loop(Array) operation */
if (parg->count) { if (parg->count) {
if (scode->op != FETCH_OP_ST_MEM && 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) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING); BAD_STRING);
ret = -EINVAL; ret = -EINVAL;
......
...@@ -96,6 +96,7 @@ enum fetch_op { ...@@ -96,6 +96,7 @@ enum fetch_op {
FETCH_OP_ST_RAW, /* Raw: .size */ FETCH_OP_ST_RAW, /* Raw: .size */
FETCH_OP_ST_MEM, /* Mem: .offset, .size */ FETCH_OP_ST_MEM, /* Mem: .offset, .size */
FETCH_OP_ST_STRING, /* String: .offset, .size */ FETCH_OP_ST_STRING, /* String: .offset, .size */
FETCH_OP_ST_USTRING, /* User String: .offset, .size */
// Stage 4 (modify) op // Stage 4 (modify) op
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
// Stage 5 (loop) op // Stage 5 (loop) op
......
...@@ -59,6 +59,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, ...@@ -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_strlen(unsigned long addr);
static nokprobe_inline int static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base); 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 static nokprobe_inline int
probe_mem_read(void *dest, void *src, size_t size); 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, ...@@ -91,6 +94,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
ret = fetch_store_strlen(val + code->offset); ret = fetch_store_strlen(val + code->offset);
code++; code++;
goto array; goto array;
} else if (code->op == FETCH_OP_ST_USTRING) {
ret += fetch_store_strlen_user(val + code->offset);
code++;
goto array;
} else } else
return -EILSEQ; return -EILSEQ;
} }
...@@ -106,6 +113,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -106,6 +113,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
loc = *(u32 *)dest; loc = *(u32 *)dest;
ret = fetch_store_string(val + code->offset, dest, base); ret = fetch_store_string(val + code->offset, dest, base);
break; break;
case FETCH_OP_ST_USTRING:
loc = *(u32 *)dest;
ret = fetch_store_string_user(val + code->offset, dest, base);
break;
default: default:
return -EILSEQ; return -EILSEQ;
} }
...@@ -123,7 +134,8 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -123,7 +134,8 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
total += ret; total += ret;
if (++i < code->param) { if (++i < code->param) {
code = s3; 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; dest += s3->size;
val += s3->size; val += s3->size;
goto stage3; goto stage3;
......
...@@ -176,6 +176,12 @@ fetch_store_string(unsigned long addr, void *dest, void *base) ...@@ -176,6 +176,12 @@ fetch_store_string(unsigned long addr, void *dest, void *base)
return ret; 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 */ /* Return the length of string -- including null terminal byte */
static nokprobe_inline int static nokprobe_inline int
fetch_store_strlen(unsigned long addr) fetch_store_strlen(unsigned long addr)
...@@ -191,6 +197,12 @@ fetch_store_strlen(unsigned long addr) ...@@ -191,6 +197,12 @@ fetch_store_strlen(unsigned long addr)
return (len > MAX_STRING_SIZE) ? 0 : len; 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) static unsigned long translate_user_vaddr(unsigned long file_offset)
{ {
unsigned long base_addr; unsigned long base_addr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment