diff options
Diffstat (limited to 'kernel/trace/trace_kprobe.c')
| -rw-r--r-- | kernel/trace/trace_kprobe.c | 111 |
1 files changed, 106 insertions, 5 deletions
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 2dec9bcde8b4..8435b43b1782 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
| @@ -353,6 +353,43 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) | |||
| 353 | kfree(data); | 353 | kfree(data); |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | /* Bitfield fetch function */ | ||
| 357 | struct bitfield_fetch_param { | ||
| 358 | struct fetch_param orig; | ||
| 359 | unsigned char hi_shift; | ||
| 360 | unsigned char low_shift; | ||
| 361 | }; | ||
| 362 | |||
| 363 | #define DEFINE_FETCH_bitfield(type) \ | ||
| 364 | static __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,\ | ||
| 365 | void *data, void *dest) \ | ||
| 366 | { \ | ||
| 367 | struct bitfield_fetch_param *bprm = data; \ | ||
| 368 | type buf = 0; \ | ||
| 369 | call_fetch(&bprm->orig, regs, &buf); \ | ||
| 370 | if (buf) { \ | ||
| 371 | buf <<= bprm->hi_shift; \ | ||
| 372 | buf >>= bprm->low_shift; \ | ||
| 373 | } \ | ||
| 374 | *(type *)dest = buf; \ | ||
| 375 | } | ||
| 376 | DEFINE_BASIC_FETCH_FUNCS(bitfield) | ||
| 377 | #define fetch_bitfield_string NULL | ||
| 378 | #define fetch_bitfield_string_size NULL | ||
| 379 | |||
| 380 | static __kprobes void | ||
| 381 | free_bitfield_fetch_param(struct bitfield_fetch_param *data) | ||
| 382 | { | ||
| 383 | /* | ||
| 384 | * Don't check the bitfield itself, because this must be the | ||
| 385 | * last fetch function. | ||
| 386 | */ | ||
| 387 | if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) | ||
| 388 | free_deref_fetch_param(data->orig.data); | ||
| 389 | else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn)) | ||
| 390 | free_symbol_cache(data->orig.data); | ||
| 391 | kfree(data); | ||
| 392 | } | ||
| 356 | /* Default (unsigned long) fetch type */ | 393 | /* Default (unsigned long) fetch type */ |
| 357 | #define __DEFAULT_FETCH_TYPE(t) u##t | 394 | #define __DEFAULT_FETCH_TYPE(t) u##t |
| 358 | #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) | 395 | #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) |
| @@ -367,6 +404,7 @@ enum { | |||
| 367 | FETCH_MTD_memory, | 404 | FETCH_MTD_memory, |
| 368 | FETCH_MTD_symbol, | 405 | FETCH_MTD_symbol, |
| 369 | FETCH_MTD_deref, | 406 | FETCH_MTD_deref, |
| 407 | FETCH_MTD_bitfield, | ||
| 370 | FETCH_MTD_END, | 408 | FETCH_MTD_END, |
| 371 | }; | 409 | }; |
| 372 | 410 | ||
| @@ -387,6 +425,7 @@ ASSIGN_FETCH_FUNC(retval, ftype), \ | |||
| 387 | ASSIGN_FETCH_FUNC(memory, ftype), \ | 425 | ASSIGN_FETCH_FUNC(memory, ftype), \ |
| 388 | ASSIGN_FETCH_FUNC(symbol, ftype), \ | 426 | ASSIGN_FETCH_FUNC(symbol, ftype), \ |
| 389 | ASSIGN_FETCH_FUNC(deref, ftype), \ | 427 | ASSIGN_FETCH_FUNC(deref, ftype), \ |
| 428 | ASSIGN_FETCH_FUNC(bitfield, ftype), \ | ||
| 390 | } \ | 429 | } \ |
| 391 | } | 430 | } |
| 392 | 431 | ||
| @@ -430,9 +469,33 @@ static const struct fetch_type *find_fetch_type(const char *type) | |||
| 430 | if (!type) | 469 | if (!type) |
| 431 | type = DEFAULT_FETCH_TYPE_STR; | 470 | type = DEFAULT_FETCH_TYPE_STR; |
| 432 | 471 | ||
| 472 | /* Special case: bitfield */ | ||
| 473 | if (*type == 'b') { | ||
| 474 | unsigned long bs; | ||
| 475 | type = strchr(type, '/'); | ||
| 476 | if (!type) | ||
| 477 | goto fail; | ||
| 478 | type++; | ||
| 479 | if (strict_strtoul(type, 0, &bs)) | ||
| 480 | goto fail; | ||
| 481 | switch (bs) { | ||
| 482 | case 8: | ||
| 483 | return find_fetch_type("u8"); | ||
| 484 | case 16: | ||
| 485 | return find_fetch_type("u16"); | ||
| 486 | case 32: | ||
| 487 | return find_fetch_type("u32"); | ||
| 488 | case 64: | ||
| 489 | return find_fetch_type("u64"); | ||
| 490 | default: | ||
| 491 | goto fail; | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 433 | for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) | 495 | for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++) |
| 434 | if (strcmp(type, fetch_type_table[i].name) == 0) | 496 | if (strcmp(type, fetch_type_table[i].name) == 0) |
| 435 | return &fetch_type_table[i]; | 497 | return &fetch_type_table[i]; |
| 498 | fail: | ||
| 436 | return NULL; | 499 | return NULL; |
| 437 | } | 500 | } |
| 438 | 501 | ||
| @@ -586,7 +649,9 @@ error: | |||
| 586 | 649 | ||
| 587 | static void free_probe_arg(struct probe_arg *arg) | 650 | static void free_probe_arg(struct probe_arg *arg) |
| 588 | { | 651 | { |
| 589 | if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) | 652 | if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) |
| 653 | free_bitfield_fetch_param(arg->fetch.data); | ||
| 654 | else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn)) | ||
| 590 | free_deref_fetch_param(arg->fetch.data); | 655 | free_deref_fetch_param(arg->fetch.data); |
| 591 | else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) | 656 | else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn)) |
| 592 | free_symbol_cache(arg->fetch.data); | 657 | free_symbol_cache(arg->fetch.data); |
| @@ -767,16 +832,15 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, | |||
| 767 | } | 832 | } |
| 768 | break; | 833 | break; |
| 769 | case '+': /* deref memory */ | 834 | case '+': /* deref memory */ |
| 835 | arg++; /* Skip '+', because strict_strtol() rejects it. */ | ||
| 770 | case '-': | 836 | case '-': |
| 771 | tmp = strchr(arg, '('); | 837 | tmp = strchr(arg, '('); |
| 772 | if (!tmp) | 838 | if (!tmp) |
| 773 | break; | 839 | break; |
| 774 | *tmp = '\0'; | 840 | *tmp = '\0'; |
| 775 | ret = strict_strtol(arg + 1, 0, &offset); | 841 | ret = strict_strtol(arg, 0, &offset); |
| 776 | if (ret) | 842 | if (ret) |
| 777 | break; | 843 | break; |
| 778 | if (arg[0] == '-') | ||
| 779 | offset = -offset; | ||
| 780 | arg = tmp + 1; | 844 | arg = tmp + 1; |
| 781 | tmp = strrchr(arg, ')'); | 845 | tmp = strrchr(arg, ')'); |
| 782 | if (tmp) { | 846 | if (tmp) { |
| @@ -807,6 +871,41 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t, | |||
| 807 | return ret; | 871 | return ret; |
| 808 | } | 872 | } |
| 809 | 873 | ||
| 874 | #define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long)) | ||
| 875 | |||
| 876 | /* Bitfield type needs to be parsed into a fetch function */ | ||
| 877 | static int __parse_bitfield_probe_arg(const char *bf, | ||
| 878 | const struct fetch_type *t, | ||
| 879 | struct fetch_param *f) | ||
| 880 | { | ||
| 881 | struct bitfield_fetch_param *bprm; | ||
| 882 | unsigned long bw, bo; | ||
| 883 | char *tail; | ||
| 884 | |||
| 885 | if (*bf != 'b') | ||
| 886 | return 0; | ||
| 887 | |||
| 888 | bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); | ||
| 889 | if (!bprm) | ||
| 890 | return -ENOMEM; | ||
| 891 | bprm->orig = *f; | ||
| 892 | f->fn = t->fetch[FETCH_MTD_bitfield]; | ||
| 893 | f->data = (void *)bprm; | ||
| 894 | |||
| 895 | bw = simple_strtoul(bf + 1, &tail, 0); /* Use simple one */ | ||
| 896 | if (bw == 0 || *tail != '@') | ||
| 897 | return -EINVAL; | ||
| 898 | |||
| 899 | bf = tail + 1; | ||
| 900 | bo = simple_strtoul(bf, &tail, 0); | ||
| 901 | if (tail == bf || *tail != '/') | ||
| 902 | return -EINVAL; | ||
| 903 | |||
| 904 | bprm->hi_shift = BYTES_TO_BITS(t->size) - (bw + bo); | ||
| 905 | bprm->low_shift = bprm->hi_shift + bo; | ||
| 906 | return (BYTES_TO_BITS(t->size) < (bw + bo)) ? -EINVAL : 0; | ||
| 907 | } | ||
| 908 | |||
| 810 | /* String length checking wrapper */ | 909 | /* String length checking wrapper */ |
| 811 | static int parse_probe_arg(char *arg, struct trace_probe *tp, | 910 | static int parse_probe_arg(char *arg, struct trace_probe *tp, |
| 812 | struct probe_arg *parg, int is_return) | 911 | struct probe_arg *parg, int is_return) |
| @@ -836,6 +935,8 @@ static int parse_probe_arg(char *arg, struct trace_probe *tp, | |||
| 836 | parg->offset = tp->size; | 935 | parg->offset = tp->size; |
| 837 | tp->size += parg->type->size; | 936 | tp->size += parg->type->size; |
| 838 | ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); | 937 | ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return); |
| 938 | if (ret >= 0 && t != NULL) | ||
| 939 | ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch); | ||
| 839 | if (ret >= 0) { | 940 | if (ret >= 0) { |
| 840 | parg->fetch_size.fn = get_fetch_size_function(parg->type, | 941 | parg->fetch_size.fn = get_fetch_size_function(parg->type, |
| 841 | parg->fetch.fn); | 942 | parg->fetch.fn); |
| @@ -1130,7 +1231,7 @@ static int command_trace_probe(const char *buf) | |||
| 1130 | return ret; | 1231 | return ret; |
| 1131 | } | 1232 | } |
| 1132 | 1233 | ||
| 1133 | #define WRITE_BUFSIZE 128 | 1234 | #define WRITE_BUFSIZE 4096 |
| 1134 | 1235 | ||
| 1135 | static ssize_t probes_write(struct file *file, const char __user *buffer, | 1236 | static ssize_t probes_write(struct file *file, const char __user *buffer, |
| 1136 | size_t count, loff_t *ppos) | 1237 | size_t count, loff_t *ppos) |
