X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=lttng-filter-interpreter.c;h=e13146241ae36e4631d27319513b8f055b5194ca;hb=d7921a5faa41a160c2679bb130e31c79ee8641f2;hp=4018264d196bc6bdcc00533154abee3db42f4705;hpb=07dfc1d0e4b093ad02682499a702dc11e54e8302;p=lttng-modules.git diff --git a/lttng-filter-interpreter.c b/lttng-filter-interpreter.c index 4018264d..e1314624 100644 --- a/lttng-filter-interpreter.c +++ b/lttng-filter-interpreter.c @@ -3,38 +3,77 @@ * * LTTng modules filter interpreter. * - * Copyright (C) 2010-2014 Mathieu Desnoyers + * Copyright (C) 2010-2016 Mathieu Desnoyers * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; only - * version 2.1 of the License. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ -#include "lttng-filter.h" +#include +#include +#include + +#include +#include + +LTTNG_STACK_FRAME_NON_STANDARD(lttng_filter_interpret_bytecode); + +/* + * get_char should be called with page fault handler disabled if it is expected + * to handle user-space read. + */ +static +char get_char(struct estack_entry *reg, size_t offset) +{ + if (unlikely(offset >= reg->u.s.seq_len)) + return '\0'; + if (reg->u.s.user) { + char c; + + /* Handle invalid access as end of string. */ + if (unlikely(!access_ok(VERIFY_READ, + reg->u.s.user_str + offset, + sizeof(c)))) + return '\0'; + /* Handle fault (nonzero return value) as end of string. */ + if (unlikely(__copy_from_user_inatomic(&c, + reg->u.s.user_str + offset, + sizeof(c)))) + return '\0'; + return c; + } else { + return reg->u.s.str[offset]; + } +} /* * -1: wildcard found. * -2: unknown escape char. * 0: normal char. */ - static -int parse_char(const char **p) +int parse_char(struct estack_entry *reg, char *c, size_t *offset) { - switch (**p) { + switch (*c) { case '\\': - (*p)++; - switch (**p) { + (*offset)++; + *c = get_char(reg, *offset); + switch (*c) { case '\\': case '*': return 0; @@ -48,74 +87,154 @@ int parse_char(const char **p) } } +static +char get_char_at_cb(size_t at, void *data) +{ + return get_char(data, at); +} + +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; + + 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(); + } + + /* Find out which side is the pattern vs. the candidate. */ + if (estack_ax(stack, top)->u.s.literal_type == ESTACK_STRING_LITERAL_TYPE_STAR_GLOB) { + pattern_reg = estack_ax(stack, top); + candidate_reg = estack_bx(stack, top); + } else { + pattern_reg = estack_bx(stack, top); + candidate_reg = estack_ax(stack, top); + } + + /* 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) { + pagefault_enable(); + set_fs(old_fs); + } + + return result; +} + static int stack_strcmp(struct estack *stack, int top, const char *cmp_type) { - const char *p = estack_bx(stack, top)->u.s.str, *q = estack_ax(stack, top)->u.s.str; - int ret; - int diff; + 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(); + } for (;;) { + int ret; int escaped_r0 = 0; + char char_bx, char_ax; + + char_bx = get_char(estack_bx(stack, top), offset_bx); + char_ax = get_char(estack_ax(stack, top), offset_ax); - if (unlikely(p - estack_bx(stack, top)->u.s.str > estack_bx(stack, top)->u.s.seq_len || *p == '\0')) { - if (q - estack_ax(stack, top)->u.s.str > estack_ax(stack, top)->u.s.seq_len || *q == '\0') { - return 0; + if (unlikely(char_bx == '\0')) { + if (char_ax == '\0') { + diff = 0; + break; } else { - if (estack_ax(stack, top)->u.s.literal) { - ret = parse_char(&q); - if (ret == -1) - return 0; + if (estack_ax(stack, top)->u.s.literal_type == + ESTACK_STRING_LITERAL_TYPE_PLAIN) { + ret = parse_char(estack_ax(stack, top), + &char_ax, &offset_ax); + if (ret == -1) { + diff = 0; + break; + } } - return -1; + diff = -1; + break; } } - if (unlikely(q - estack_ax(stack, top)->u.s.str > estack_ax(stack, top)->u.s.seq_len || *q == '\0')) { - if (p - estack_bx(stack, top)->u.s.str > estack_bx(stack, top)->u.s.seq_len || *p == '\0') { - return 0; - } else { - if (estack_bx(stack, top)->u.s.literal) { - ret = parse_char(&p); - if (ret == -1) - return 0; + if (unlikely(char_ax == '\0')) { + if (estack_bx(stack, top)->u.s.literal_type == + ESTACK_STRING_LITERAL_TYPE_PLAIN) { + ret = parse_char(estack_bx(stack, top), + &char_bx, &offset_bx); + if (ret == -1) { + diff = 0; + break; } - return 1; } + diff = 1; + break; } - if (estack_bx(stack, top)->u.s.literal) { - ret = parse_char(&p); + if (estack_bx(stack, top)->u.s.literal_type == + ESTACK_STRING_LITERAL_TYPE_PLAIN) { + ret = parse_char(estack_bx(stack, top), + &char_bx, &offset_bx); if (ret == -1) { - return 0; + diff = 0; + break; } else if (ret == -2) { escaped_r0 = 1; } /* else compare both char */ } - if (estack_ax(stack, top)->u.s.literal) { - ret = parse_char(&q); + if (estack_ax(stack, top)->u.s.literal_type == + ESTACK_STRING_LITERAL_TYPE_PLAIN) { + ret = parse_char(estack_ax(stack, top), + &char_ax, &offset_ax); if (ret == -1) { - return 0; + diff = 0; + break; } else if (ret == -2) { - if (!escaped_r0) - return -1; + if (!escaped_r0) { + diff = -1; + break; + } } else { - if (escaped_r0) - return 1; + if (escaped_r0) { + diff = 1; + break; + } } } else { - if (escaped_r0) - return 1; + if (escaped_r0) { + diff = 1; + break; + } } - diff = *p - *q; + diff = char_bx - char_ax; if (diff != 0) break; - p++; - q++; + offset_bx++; + offset_ax++; + } + if (has_user) { + pagefault_enable(); + set_fs(old_fs); } return diff; } uint64_t lttng_filter_false(void *filter_data, + struct lttng_probe_ctx *lttng_probe_ctx, const char *filter_stack_data) { return 0; @@ -173,6 +292,7 @@ LABEL_##name * effect. */ uint64_t lttng_filter_interpret_bytecode(void *filter_data, + struct lttng_probe_ctx *lttng_probe_ctx, const char *filter_stack_data) { struct bytecode_runtime *bytecode = filter_data; @@ -217,6 +337,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, [ FILTER_OP_GE_STRING ] = &&LABEL_FILTER_OP_GE_STRING, [ FILTER_OP_LE_STRING ] = &&LABEL_FILTER_OP_LE_STRING, + /* globbing pattern binary comparator */ + [ FILTER_OP_EQ_STAR_GLOB_STRING ] = &&LABEL_FILTER_OP_EQ_STAR_GLOB_STRING, + [ FILTER_OP_NE_STAR_GLOB_STRING ] = &&LABEL_FILTER_OP_NE_STAR_GLOB_STRING, + /* s64 binary comparator */ [ FILTER_OP_EQ_S64 ] = &&LABEL_FILTER_OP_EQ_S64, [ FILTER_OP_NE_S64 ] = &&LABEL_FILTER_OP_NE_S64, @@ -272,6 +396,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, /* load from immediate operand */ [ FILTER_OP_LOAD_STRING ] = &&LABEL_FILTER_OP_LOAD_STRING, + [ FILTER_OP_LOAD_STAR_GLOB_STRING ] = &&LABEL_FILTER_OP_LOAD_STAR_GLOB_STRING, [ FILTER_OP_LOAD_S64 ] = &&LABEL_FILTER_OP_LOAD_S64, [ FILTER_OP_LOAD_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_DOUBLE, @@ -285,6 +410,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, [ FILTER_OP_GET_CONTEXT_REF_STRING ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_STRING, [ FILTER_OP_GET_CONTEXT_REF_S64 ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_S64, [ FILTER_OP_GET_CONTEXT_REF_DOUBLE ] = &&LABEL_FILTER_OP_GET_CONTEXT_REF_DOUBLE, + + /* load userspace field ref */ + [ FILTER_OP_LOAD_FIELD_REF_USER_STRING ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_USER_STRING, + [ FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE, }; #endif /* #ifndef INTERPRETER_USE_SWITCH */ @@ -395,6 +524,27 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, PO; } + OP(FILTER_OP_EQ_STAR_GLOB_STRING): + { + int res; + + res = (stack_star_glob_match(stack, top, "==") == 0); + estack_pop(stack, top, ax, bx); + estack_ax_v = res; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_NE_STAR_GLOB_STRING): + { + int res; + + res = (stack_star_glob_match(stack, top, "!=") != 0); + estack_pop(stack, top, ax, bx); + estack_ax_v = res; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_EQ_S64): { int res; @@ -577,8 +727,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, ret = -EINVAL; goto end; } - estack_ax(stack, top)->u.s.seq_len = UINT_MAX; - estack_ax(stack, top)->u.s.literal = 0; + 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; dbg_printk("ref load string %s\n", estack_ax(stack, top)->u.s.str); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -602,7 +754,9 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, ret = -EINVAL; goto end; } - estack_ax(stack, top)->u.s.literal = 0; + 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) + sizeof(struct field_ref); PO; } @@ -637,8 +791,25 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printk("load string %s\n", insn->data); estack_push(stack, top, ax, bx); estack_ax(stack, top)->u.s.str = insn->data; - estack_ax(stack, top)->u.s.seq_len = UINT_MAX; - estack_ax(stack, top)->u.s.literal = 1; + estack_ax(stack, top)->u.s.seq_len = LTTNG_SIZE_MAX; + estack_ax(stack, top)->u.s.literal_type = + ESTACK_STRING_LITERAL_TYPE_PLAIN; + estack_ax(stack, top)->u.s.user = 0; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + PO; + } + + OP(FILTER_OP_LOAD_STAR_GLOB_STRING): + { + struct load_op *insn = (struct load_op *) pc; + + dbg_printk("load globbing pattern %s\n", insn->data); + estack_push(stack, top, ax, bx); + estack_ax(stack, top)->u.s.str = insn->data; + estack_ax(stack, top)->u.s.seq_len = LTTNG_SIZE_MAX; + estack_ax(stack, top)->u.s.literal_type = + ESTACK_STRING_LITERAL_TYPE_STAR_GLOB; + estack_ax(stack, top)->u.s.user = 0; next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; PO; } @@ -692,7 +863,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printk("get context ref offset %u type string\n", ref->offset); ctx_field = <tng_static_ctx->fields[ref->offset]; - ctx_field->get_value(ctx_field, &v); + ctx_field->get_value(ctx_field, lttng_probe_ctx, &v); estack_push(stack, top, ax, bx); estack_ax(stack, top)->u.s.str = v.str; if (unlikely(!estack_ax(stack, top)->u.s.str)) { @@ -700,8 +871,10 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, ret = -EINVAL; goto end; } - estack_ax(stack, top)->u.s.seq_len = UINT_MAX; - estack_ax(stack, top)->u.s.literal = 0; + 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; dbg_printk("ref get context string %s\n", estack_ax(stack, top)->u.s.str); next_pc += sizeof(struct load_op) + sizeof(struct field_ref); PO; @@ -717,7 +890,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, dbg_printk("get context ref offset %u type s64\n", ref->offset); ctx_field = <tng_static_ctx->fields[ref->offset]; - ctx_field->get_value(ctx_field, &v); + ctx_field->get_value(ctx_field, lttng_probe_ctx, &v); estack_push(stack, top, ax, bx); estack_ax_v = v.s64; dbg_printk("ref get context s64 %lld\n", @@ -732,6 +905,56 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, PO; } + /* load userspace field ref */ + OP(FILTER_OP_LOAD_FIELD_REF_USER_STRING): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printk("load field ref offset %u type user string\n", + ref->offset); + 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)) { + dbg_printk("Filter warning: loading a NULL string.\n"); + ret = -EINVAL; + goto end; + } + 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 = 1; + dbg_printk("ref load string %s\n", estack_ax(stack, top)->u.s.str); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + + OP(FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printk("load field ref offset %u type user sequence\n", + ref->offset); + estack_push(stack, top, ax, bx); + estack_ax(stack, top)->u.s.seq_len = + *(unsigned long *) &filter_stack_data[ref->offset]; + 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)) { + dbg_printk("Filter warning: loading a NULL sequence.\n"); + ret = -EINVAL; + goto end; + } + estack_ax(stack, top)->u.s.literal_type = + ESTACK_STRING_LITERAL_TYPE_NONE; + estack_ax(stack, top)->u.s.user = 1; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + END_OP end: /* return 0 (discard) on error */