]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
tracing/probe: Add ustring type for user-space string
authorMasami Hiramatsu <mhiramat@kernel.org>
Wed, 15 May 2019 05:38:30 +0000 (14:38 +0900)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Sun, 26 May 2019 03:04:42 +0000 (23:04 -0400)
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@devnote2
Acked-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Documentation/trace/kprobetrace.rst
kernel/trace/trace.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h
kernel/trace/trace_probe_tmpl.h
kernel/trace/trace_uprobe.c

index 235ce2ab131a71dbfa0cc5f92d176794231f0aa2..a3ac7c9ac24241e5bf1bbdea096a8d2940a6d1f6 100644 (file)
@@ -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, <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
index 1c80521fd43602041d797032a26cac79f665314c..d3a477a16e7082591e188912a87e93aeb70301b0 100644 (file)
@@ -4847,7 +4847,7 @@ static const char readme_msg[] =
        "\t           $stack<index>, $stack, $retval, $comm\n"
 #endif
        "\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"
 #ifdef CONFIG_HIST_TRIGGERS
        "\t    field: <stype> <name>;\n"
index 7d736248a070b2f633349856345e2013d4a3cd34..439bf04d14ce66134e9e0b17ddb438dfc5bc816c 100644 (file)
@@ -886,6 +886,15 @@ 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)
+{
+       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
  * length and relative data location.
@@ -894,19 +903,46 @@ static nokprobe_inline int
 fetch_store_string(unsigned long addr, void *dest, void *base)
 {
        int maxlen = get_loc_len(*(u32 *)dest);
-       u8 *dst = get_loc_data(dest, base);
+       void *__dest;
        long ret;
 
        if (unlikely(!maxlen))
                return -ENOMEM;
+
+       __dest = get_loc_data(dest, base);
+
        /*
         * Try to get string again, since the string can be changed while
         * 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)
-               *(u32 *)dest = make_data_loc(ret, (void *)dst - base);
+               *(u32 *)dest = make_data_loc(ret, __dest - base);
+
        return ret;
 }
 
index a347faced9595092464744b9729c80145ea8e0c8..5a0470f7b9de00caac4912e673ba8b497ca73468 100644 (file)
@@ -78,6 +78,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),
@@ -569,7 +571,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) {
                        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,
                                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) {
@@ -618,7 +625,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) {
                        trace_probe_log_err(offset + (t ? (t - arg) : 0),
                                            BAD_STRING);
                        ret = -EINVAL;
index f9a8c632188bcf8a3d849cdf94f2d9a11a081eb6..c7546e7ff8e24128cac78418af0b43a9d453ba32 100644 (file)
@@ -96,6 +96,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
index c30c61f12dddff4b1e791751c2029a34bd182b93..2e9e4dae883995f48578fcc164570cdc22fafdcc 100644 (file)
@@ -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;
index eb7e06b54741beec641f536a645597698a5df986..852e998051f68f433f23cf2faa0fe8973ad179ef 100644 (file)
@@ -176,6 +176,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)
@@ -191,6 +197,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;