X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=lttng-filter-interpreter.c;h=a9af74ad0a2c2b8697693c6a91d40e0ef97c0c85;hb=refs%2Fheads%2Fstable-2.12;hp=3dad6cc6650830f2990ec410f09b523dd59573d3;hpb=80bb26003945e96a8ade9c8788dab9b2e08cbc08;p=lttng-modules.git diff --git a/lttng-filter-interpreter.c b/lttng-filter-interpreter.c index 3dad6cc6..a9af74ad 100644 --- a/lttng-filter-interpreter.c +++ b/lttng-filter-interpreter.c @@ -7,13 +7,15 @@ * Copyright (C) 2010-2016 Mathieu Desnoyers */ +#include #include -#include +#include #include #include #include #include +#include LTTNG_STACK_FRAME_NON_STANDARD(lttng_filter_interpret_bytecode); @@ -22,7 +24,7 @@ LTTNG_STACK_FRAME_NON_STANDARD(lttng_filter_interpret_bytecode); * to handle user-space read. */ static -char get_char(struct estack_entry *reg, size_t offset) +char get_char(const struct estack_entry *reg, size_t offset) { if (unlikely(offset >= reg->u.s.seq_len)) return '\0'; @@ -81,16 +83,14 @@ static int stack_star_glob_match(struct estack *stack, int top, const char *cmp_type) { bool has_user = false; - mm_segment_t old_fs; int result; struct estack_entry *pattern_reg; struct estack_entry *candidate_reg; + /* Disable the page fault handler when reading from userspace. */ if (estack_bx(stack, top)->u.s.user || estack_ax(stack, top)->u.s.user) { has_user = true; - old_fs = get_fs(); - set_fs(KERNEL_DS); pagefault_disable(); } @@ -106,10 +106,8 @@ int stack_star_glob_match(struct estack *stack, int top, const char *cmp_type) /* Perform the match operation. */ result = !strutils_star_glob_match_char_cb(get_char_at_cb, pattern_reg, get_char_at_cb, candidate_reg); - if (has_user) { + if (has_user) pagefault_enable(); - set_fs(old_fs); - } return result; } @@ -119,13 +117,10 @@ int stack_strcmp(struct estack *stack, int top, const char *cmp_type) { size_t offset_bx = 0, offset_ax = 0; int diff, has_user = 0; - mm_segment_t old_fs; if (estack_bx(stack, top)->u.s.user || estack_ax(stack, top)->u.s.user) { has_user = 1; - old_fs = get_fs(); - set_fs(KERNEL_DS); pagefault_disable(); } @@ -210,10 +205,9 @@ int stack_strcmp(struct estack *stack, int top, const char *cmp_type) offset_bx++; offset_ax++; } - if (has_user) { + if (has_user) pagefault_enable(); - set_fs(old_fs); - } + return diff; } @@ -297,6 +291,8 @@ static int context_get_index(struct lttng_probe_ctx *lttng_probe_ctx, ptr->u.u64 = v.s64; /* Cast. */ ptr->ptr = &ptr->u.u64; } + ptr->rev_bo = field->type.u.basic.integer.reverse_byte_order; + ptr->user = field->type.u.basic.integer.user; break; case atype_enum: { @@ -313,6 +309,8 @@ static int context_get_index(struct lttng_probe_ctx *lttng_probe_ctx, ptr->u.u64 = v.s64; /* Cast. */ ptr->ptr = &ptr->u.u64; } + ptr->rev_bo = itype->reverse_byte_order; + ptr->user = itype->user; break; } case atype_array: @@ -327,6 +325,7 @@ static int context_get_index(struct lttng_probe_ctx *lttng_probe_ctx, ptr->object_type = OBJECT_TYPE_STRING; ctx_field->get_value(ctx_field, lttng_probe_ctx, &v); ptr->ptr = v.str; + ptr->user = field->type.u.array.elem_type.u.basic.integer.user; break; case atype_sequence: if (field->type.u.sequence.elem_type.atype != atype_integer) { @@ -340,6 +339,7 @@ static int context_get_index(struct lttng_probe_ctx *lttng_probe_ctx, ptr->object_type = OBJECT_TYPE_STRING; ctx_field->get_value(ctx_field, lttng_probe_ctx, &v); ptr->ptr = v.str; + ptr->user = field->type.u.sequence.elem_type.u.basic.integer.user; break; case atype_array_bitfield: printk(KERN_WARNING "Bitfield array type is not supported.\n"); @@ -351,6 +351,7 @@ static int context_get_index(struct lttng_probe_ctx *lttng_probe_ctx, ptr->object_type = OBJECT_TYPE_STRING; ctx_field->get_value(ctx_field, lttng_probe_ctx, &v); ptr->ptr = v.str; + ptr->user = field->type.u.basic.string.user; break; case atype_struct: printk(KERN_WARNING "Structure type cannot be loaded.\n"); @@ -391,6 +392,7 @@ static int dynamic_get_index(struct lttng_probe_ctx *lttng_probe_ctx, stack_top->u.ptr.ptr = ptr; stack_top->u.ptr.object_type = gid->elem.type; stack_top->u.ptr.rev_bo = gid->elem.rev_bo; + stack_top->u.ptr.user = gid->elem.user; /* field is only used for types nested within variants. */ stack_top->u.ptr.field = NULL; break; @@ -410,6 +412,7 @@ static int dynamic_get_index(struct lttng_probe_ctx *lttng_probe_ctx, stack_top->u.ptr.ptr = ptr; stack_top->u.ptr.object_type = gid->elem.type; stack_top->u.ptr.rev_bo = gid->elem.rev_bo; + stack_top->u.ptr.user = gid->elem.user; /* field is only used for types nested within variants. */ stack_top->u.ptr.field = NULL; break; @@ -427,7 +430,8 @@ static int dynamic_get_index(struct lttng_probe_ctx *lttng_probe_ctx, } break; case LOAD_ROOT_CONTEXT: - case LOAD_ROOT_APP_CONTEXT: /* Fall-through */ + lttng_fallthrough; + case LOAD_ROOT_APP_CONTEXT: { ret = context_get_index(lttng_probe_ctx, &stack_top->u.ptr, @@ -445,6 +449,8 @@ static int dynamic_get_index(struct lttng_probe_ctx *lttng_probe_ctx, stack_top->u.ptr.type = LOAD_OBJECT; /* field is only used for types nested within variants. */ stack_top->u.ptr.field = NULL; + stack_top->u.ptr.rev_bo = gid->elem.rev_bo; + stack_top->u.ptr.user = gid->elem.user; break; } return 0; @@ -471,14 +477,24 @@ static int dynamic_load_field(struct estack_entry *stack_top) switch (stack_top->u.ptr.object_type) { case OBJECT_TYPE_S8: dbg_printk("op load field s8\n"); - stack_top->u.v = *(int8_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&stack_top->u.v, (int8_t __user *) stack_top->u.ptr.ptr, sizeof(int8_t))) + stack_top->u.v = 0; + } else { + stack_top->u.v = *(int8_t *) stack_top->u.ptr.ptr; + } break; case OBJECT_TYPE_S16: { int16_t tmp; dbg_printk("op load field s16\n"); - tmp = *(int16_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (int16_t __user *) stack_top->u.ptr.ptr, sizeof(int16_t))) + tmp = 0; + } else { + tmp = *(int16_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab16s(&tmp); stack_top->u.v = tmp; @@ -489,7 +505,12 @@ static int dynamic_load_field(struct estack_entry *stack_top) int32_t tmp; dbg_printk("op load field s32\n"); - tmp = *(int32_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (int32_t __user *) stack_top->u.ptr.ptr, sizeof(int32_t))) + tmp = 0; + } else { + tmp = *(int32_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab32s(&tmp); stack_top->u.v = tmp; @@ -500,7 +521,12 @@ static int dynamic_load_field(struct estack_entry *stack_top) int64_t tmp; dbg_printk("op load field s64\n"); - tmp = *(int64_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (int64_t __user *) stack_top->u.ptr.ptr, sizeof(int64_t))) + tmp = 0; + } else { + tmp = *(int64_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab64s(&tmp); stack_top->u.v = tmp; @@ -508,14 +534,24 @@ static int dynamic_load_field(struct estack_entry *stack_top) } case OBJECT_TYPE_U8: dbg_printk("op load field u8\n"); - stack_top->u.v = *(uint8_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&stack_top->u.v, (uint8_t __user *) stack_top->u.ptr.ptr, sizeof(uint8_t))) + stack_top->u.v = 0; + } else { + stack_top->u.v = *(uint8_t *) stack_top->u.ptr.ptr; + } break; case OBJECT_TYPE_U16: { uint16_t tmp; dbg_printk("op load field s16\n"); - tmp = *(uint16_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (uint16_t __user *) stack_top->u.ptr.ptr, sizeof(uint16_t))) + tmp = 0; + } else { + tmp = *(uint16_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab16s(&tmp); stack_top->u.v = tmp; @@ -526,7 +562,12 @@ static int dynamic_load_field(struct estack_entry *stack_top) uint32_t tmp; dbg_printk("op load field u32\n"); - tmp = *(uint32_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (uint32_t __user *) stack_top->u.ptr.ptr, sizeof(uint32_t))) + tmp = 0; + } else { + tmp = *(uint32_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab32s(&tmp); stack_top->u.v = tmp; @@ -537,7 +578,12 @@ static int dynamic_load_field(struct estack_entry *stack_top) uint64_t tmp; dbg_printk("op load field u64\n"); - tmp = *(uint64_t *) stack_top->u.ptr.ptr; + if (stack_top->u.ptr.user) { + if (lttng_copy_from_user_check_nofault(&tmp, (uint64_t __user *) stack_top->u.ptr.ptr, sizeof(uint64_t))) + tmp = 0; + } else { + tmp = *(uint64_t *) stack_top->u.ptr.ptr; + } if (stack_top->u.ptr.rev_bo) __swab64s(&tmp); stack_top->u.v = tmp; @@ -545,19 +591,30 @@ static int dynamic_load_field(struct estack_entry *stack_top) } case OBJECT_TYPE_STRING: { - const char *str; + dbg_printk("op load field string: user=%d\n", stack_top->u.ptr.user); + if (stack_top->u.ptr.user) { + const char __user *user_str = (const char __user *) stack_top->u.ptr.ptr; - dbg_printk("op load field string\n"); - str = (const char *) stack_top->u.ptr.ptr; - stack_top->u.s.str = str; - if (unlikely(!stack_top->u.s.str)) { - dbg_printk("Filter warning: loading a NULL string.\n"); - ret = -EINVAL; - goto end; + stack_top->u.s.user_str = user_str; + if (unlikely(!stack_top->u.s.user_str)) { + dbg_printk("Bytecode warning: loading a NULL user string.\n"); + ret = -EINVAL; + goto end; + } + stack_top->u.s.user = 1; + } else { + const char *str = (const char *) stack_top->u.ptr.ptr; + + stack_top->u.s.str = str; + if (unlikely(!stack_top->u.s.str)) { + dbg_printk("Bytecode warning: loading a NULL string.\n"); + ret = -EINVAL; + goto end; + } + stack_top->u.s.user = 0; } stack_top->u.s.seq_len = LTTNG_SIZE_MAX; - stack_top->u.s.literal_type = - ESTACK_STRING_LITERAL_TYPE_NONE; + stack_top->u.s.literal_type = ESTACK_STRING_LITERAL_TYPE_NONE; break; } case OBJECT_TYPE_STRING_SEQUENCE: @@ -567,14 +624,24 @@ static int dynamic_load_field(struct estack_entry *stack_top) dbg_printk("op load field string sequence\n"); ptr = stack_top->u.ptr.ptr; stack_top->u.s.seq_len = *(unsigned long *) ptr; - stack_top->u.s.str = *(const char **) (ptr + sizeof(unsigned long)); - if (unlikely(!stack_top->u.s.str)) { - dbg_printk("Filter warning: loading a NULL sequence.\n"); - ret = -EINVAL; - goto end; + if (stack_top->u.ptr.user) { + stack_top->u.s.user_str = *(const char __user **) (ptr + sizeof(unsigned long)); + if (unlikely(!stack_top->u.s.user_str)) { + dbg_printk("Bytecode warning: loading a NULL user sequence.\n"); + ret = -EINVAL; + goto end; + } + stack_top->u.s.user = 1; + } else { + stack_top->u.s.str = *(const char **) (ptr + sizeof(unsigned long)); + if (unlikely(!stack_top->u.s.str)) { + dbg_printk("Bytecode warning: loading a NULL sequence.\n"); + ret = -EINVAL; + goto end; + } + stack_top->u.s.user = 0; } - stack_top->u.s.literal_type = - ESTACK_STRING_LITERAL_TYPE_NONE; + stack_top->u.s.literal_type = ESTACK_STRING_LITERAL_TYPE_NONE; break; } case OBJECT_TYPE_DYNAMIC: @@ -601,6 +668,39 @@ end: return ret; } +#ifdef DEBUG + +#define DBG_USER_STR_CUTOFF 32 + +/* + * In debug mode, print user string (truncated, if necessary). + */ +static inline +void dbg_load_ref_user_str_printk(const struct estack_entry *user_str_reg) +{ + size_t pos = 0; + char last_char; + char user_str[DBG_USER_STR_CUTOFF]; + + pagefault_disable(); + do { + last_char = get_char(user_str_reg, pos); + user_str[pos] = last_char; + pos++; + } while (last_char != '\0' && pos < sizeof(user_str)); + pagefault_enable(); + + user_str[sizeof(user_str) - 1] = '\0'; + dbg_printk("load field ref user string: '%s%s'\n", user_str, + last_char != '\0' ? "[...]" : ""); +} +#else +static inline +void dbg_load_ref_user_str_printk(const struct estack_entry *user_str_reg) +{ +} +#endif + /* * Return 0 (discard), or raise the 0x1 flag (log event). * Currently, other flags are kept for future extensions and have no @@ -1321,7 +1421,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, estack_push(stack, top, ax, bx); estack_ax(stack, top)->u.s.user_str = *(const char * const *) &filter_stack_data[ref->offset]; - if (unlikely(!estack_ax(stack, top)->u.s.str)) { + if (unlikely(!estack_ax(stack, top)->u.s.user_str)) { dbg_printk("Filter warning: loading a NULL string.\n"); ret = -EINVAL; goto end; @@ -1330,7 +1430,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, estack_ax(stack, top)->u.s.literal_type = ESTACK_STRING_LITERAL_TYPE_NONE; estack_ax(stack, top)->u.s.user = 1; - dbg_printk("ref load string %s\n", estack_ax(stack, top)->u.s.str); + dbg_load_ref_user_str_printk(estack_ax(stack, top)); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; } @@ -1348,7 +1448,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, estack_ax(stack, top)->u.s.user_str = *(const char **) (&filter_stack_data[ref->offset + sizeof(unsigned long)]); - if (unlikely(!estack_ax(stack, top)->u.s.str)) { + if (unlikely(!estack_ax(stack, top)->u.s.user_str)) { dbg_printk("Filter warning: loading a NULL sequence.\n"); ret = -EINVAL; goto end; @@ -1545,6 +1645,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, estack_ax(stack, top)->u.s.seq_len = LTTNG_SIZE_MAX; estack_ax(stack, top)->u.s.literal_type = ESTACK_STRING_LITERAL_TYPE_NONE; + estack_ax(stack, top)->u.s.user = 0; next_pc += sizeof(struct load_op); PO; } @@ -1564,6 +1665,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, } estack_ax(stack, top)->u.s.literal_type = ESTACK_STRING_LITERAL_TYPE_NONE; + estack_ax(stack, top)->u.s.user = 0; next_pc += sizeof(struct load_op); PO; }