From 97b58163ffe6cced54c3d37ae79d4113a9d26455 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Mon, 16 Jul 2012 12:31:44 -0400 Subject: [PATCH] Filter: split passes into separate components Signed-off-by: Mathieu Desnoyers --- liblttng-ust/Makefile.am | 3 + liblttng-ust/lttng-filter-interpreter.c | 679 +++++++++ liblttng-ust/lttng-filter-specialize.c | 455 ++++++ liblttng-ust/lttng-filter-validator.c | 671 +++++++++ liblttng-ust/lttng-filter.c | 1809 +---------------------- liblttng-ust/lttng-filter.h | 104 ++ 6 files changed, 1913 insertions(+), 1808 deletions(-) create mode 100644 liblttng-ust/lttng-filter-interpreter.c create mode 100644 liblttng-ust/lttng-filter-specialize.c create mode 100644 liblttng-ust/lttng-filter-validator.c create mode 100644 liblttng-ust/lttng-filter.h diff --git a/liblttng-ust/Makefile.am b/liblttng-ust/Makefile.am index 78eb1cc2..9ec05915 100644 --- a/liblttng-ust/Makefile.am +++ b/liblttng-ust/Makefile.am @@ -29,6 +29,9 @@ liblttng_ust_runtime_la_SOURCES = \ ltt-context.c \ ltt-events.c \ lttng-filter.c \ + lttng-filter-validator.c \ + lttng-filter-specialize.c \ + lttng-filter-interpreter.c \ filter-bytecode.h \ tracepoint-internal.h \ clock.h \ diff --git a/liblttng-ust/lttng-filter-interpreter.c b/liblttng-ust/lttng-filter-interpreter.c new file mode 100644 index 00000000..ee3f4a34 --- /dev/null +++ b/liblttng-ust/lttng-filter-interpreter.c @@ -0,0 +1,679 @@ +/* + * lttng-filter-interpreter.c + * + * LTTng UST filter interpreter. + * + * Copyright (C) 2010-2012 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. + * + * 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. + * + * 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 + */ + +#include "lttng-filter.h" + +/* + * -1: wildcard found. + * -2: unknown escape char. + * 0: normal char. + */ + +static +int parse_char(const char **p) +{ + switch (**p) { + case '\\': + (*p)++; + switch (**p) { + case '\\': + case '*': + return 0; + default: + return -2; + } + case '*': + return -1; + default: + return 0; + } +} + +static +int reg_strcmp(struct reg reg[NR_REG], const char *cmp_type) +{ + const char *p = reg[REG_R0].str, *q = reg[REG_R1].str; + int ret; + int diff; + + for (;;) { + int escaped_r0 = 0; + + if (unlikely(p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0')) { + if (q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0') + diff = 0; + else + diff = -1; + break; + } + if (unlikely(q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0')) { + if (p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0') + diff = 0; + else + diff = 1; + break; + } + if (reg[REG_R0].literal) { + ret = parse_char(&p); + if (ret == -1) { + return 0; + } else if (ret == -2) { + escaped_r0 = 1; + } + /* else compare both char */ + } + if (reg[REG_R1].literal) { + ret = parse_char(&q); + if (ret == -1) { + return 0; + } else if (ret == -2) { + if (!escaped_r0) + return -1; + } else { + if (escaped_r0) + return 1; + } + } else { + if (escaped_r0) + return 1; + } + diff = *p - *q; + if (diff != 0) + break; + p++; + q++; + } + return diff; +} + +int lttng_filter_false(void *filter_data, + const char *filter_stack_data) +{ + return 0; +} + +#ifdef INTERPRETER_USE_SWITCH + +/* + * Fallback for compilers that do not support taking address of labels. + */ + +#define START_OP \ + start_pc = &bytecode->data[0]; \ + for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; \ + pc = next_pc) { \ + dbg_printf("Executing op %s (%u)\n", \ + print_op((unsigned int) *(filter_opcode_t *) pc), \ + (unsigned int) *(filter_opcode_t *) pc); \ + switch (*(filter_opcode_t *) pc) { + +#define OP(name) case name + +#define PO break + +#define END_OP } \ + } + +#else + +/* + * Dispatch-table based interpreter. + */ + +#define START_OP \ + start_pc = &bytecode->data[0]; \ + pc = next_pc = start_pc; \ + if (unlikely(pc - start_pc >= bytecode->len)) \ + goto end; \ + goto *dispatch[*(filter_opcode_t *) pc]; + +#define OP(name) \ +LABEL_##name + +#define PO \ + pc = next_pc; \ + goto *dispatch[*(filter_opcode_t *) pc]; + +#define END_OP + +#endif + +int lttng_filter_interpret_bytecode(void *filter_data, + const char *filter_stack_data) +{ + struct bytecode_runtime *bytecode = filter_data; + void *pc, *next_pc, *start_pc; + int ret = -EINVAL; + int retval = 0; + struct reg reg[NR_REG]; +#ifndef INTERPRETER_USE_SWITCH + static void *dispatch[NR_FILTER_OPS] = { + [ FILTER_OP_UNKNOWN ] = &&LABEL_FILTER_OP_UNKNOWN, + + [ FILTER_OP_RETURN ] = &&LABEL_FILTER_OP_RETURN, + + /* binary */ + [ FILTER_OP_MUL ] = &&LABEL_FILTER_OP_MUL, + [ FILTER_OP_DIV ] = &&LABEL_FILTER_OP_DIV, + [ FILTER_OP_MOD ] = &&LABEL_FILTER_OP_MOD, + [ FILTER_OP_PLUS ] = &&LABEL_FILTER_OP_PLUS, + [ FILTER_OP_MINUS ] = &&LABEL_FILTER_OP_MINUS, + [ FILTER_OP_RSHIFT ] = &&LABEL_FILTER_OP_RSHIFT, + [ FILTER_OP_LSHIFT ] = &&LABEL_FILTER_OP_LSHIFT, + [ FILTER_OP_BIN_AND ] = &&LABEL_FILTER_OP_BIN_AND, + [ FILTER_OP_BIN_OR ] = &&LABEL_FILTER_OP_BIN_OR, + [ FILTER_OP_BIN_XOR ] = &&LABEL_FILTER_OP_BIN_XOR, + + /* binary comparators */ + [ FILTER_OP_EQ ] = &&LABEL_FILTER_OP_EQ, + [ FILTER_OP_NE ] = &&LABEL_FILTER_OP_NE, + [ FILTER_OP_GT ] = &&LABEL_FILTER_OP_GT, + [ FILTER_OP_LT ] = &&LABEL_FILTER_OP_LT, + [ FILTER_OP_GE ] = &&LABEL_FILTER_OP_GE, + [ FILTER_OP_LE ] = &&LABEL_FILTER_OP_LE, + + /* string binary comparator */ + [ FILTER_OP_EQ_STRING ] = &&LABEL_FILTER_OP_EQ_STRING, + [ FILTER_OP_NE_STRING ] = &&LABEL_FILTER_OP_NE_STRING, + [ FILTER_OP_GT_STRING ] = &&LABEL_FILTER_OP_GT_STRING, + [ FILTER_OP_LT_STRING ] = &&LABEL_FILTER_OP_LT_STRING, + [ FILTER_OP_GE_STRING ] = &&LABEL_FILTER_OP_GE_STRING, + [ FILTER_OP_LE_STRING ] = &&LABEL_FILTER_OP_LE_STRING, + + /* s64 binary comparator */ + [ FILTER_OP_EQ_S64 ] = &&LABEL_FILTER_OP_EQ_S64, + [ FILTER_OP_NE_S64 ] = &&LABEL_FILTER_OP_NE_S64, + [ FILTER_OP_GT_S64 ] = &&LABEL_FILTER_OP_GT_S64, + [ FILTER_OP_LT_S64 ] = &&LABEL_FILTER_OP_LT_S64, + [ FILTER_OP_GE_S64 ] = &&LABEL_FILTER_OP_GE_S64, + [ FILTER_OP_LE_S64 ] = &&LABEL_FILTER_OP_LE_S64, + + /* double binary comparator */ + [ FILTER_OP_EQ_DOUBLE ] = &&LABEL_FILTER_OP_EQ_DOUBLE, + [ FILTER_OP_NE_DOUBLE ] = &&LABEL_FILTER_OP_NE_DOUBLE, + [ FILTER_OP_GT_DOUBLE ] = &&LABEL_FILTER_OP_GT_DOUBLE, + [ FILTER_OP_LT_DOUBLE ] = &&LABEL_FILTER_OP_LT_DOUBLE, + [ FILTER_OP_GE_DOUBLE ] = &&LABEL_FILTER_OP_GE_DOUBLE, + [ FILTER_OP_LE_DOUBLE ] = &&LABEL_FILTER_OP_LE_DOUBLE, + + /* unary */ + [ FILTER_OP_UNARY_PLUS ] = &&LABEL_FILTER_OP_UNARY_PLUS, + [ FILTER_OP_UNARY_MINUS ] = &&LABEL_FILTER_OP_UNARY_MINUS, + [ FILTER_OP_UNARY_NOT ] = &&LABEL_FILTER_OP_UNARY_NOT, + [ FILTER_OP_UNARY_PLUS_S64 ] = &&LABEL_FILTER_OP_UNARY_PLUS_S64, + [ FILTER_OP_UNARY_MINUS_S64 ] = &&LABEL_FILTER_OP_UNARY_MINUS_S64, + [ FILTER_OP_UNARY_NOT_S64 ] = &&LABEL_FILTER_OP_UNARY_NOT_S64, + [ FILTER_OP_UNARY_PLUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_PLUS_DOUBLE, + [ FILTER_OP_UNARY_MINUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_MINUS_DOUBLE, + [ FILTER_OP_UNARY_NOT_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_NOT_DOUBLE, + + /* logical */ + [ FILTER_OP_AND ] = &&LABEL_FILTER_OP_AND, + [ FILTER_OP_OR ] = &&LABEL_FILTER_OP_OR, + + /* load */ + [ FILTER_OP_LOAD_FIELD_REF ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF, + [ FILTER_OP_LOAD_FIELD_REF_STRING ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_STRING, + [ FILTER_OP_LOAD_FIELD_REF_SEQUENCE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_SEQUENCE, + [ FILTER_OP_LOAD_FIELD_REF_S64 ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_S64, + [ FILTER_OP_LOAD_FIELD_REF_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_DOUBLE, + + [ FILTER_OP_LOAD_STRING ] = &&LABEL_FILTER_OP_LOAD_STRING, + [ FILTER_OP_LOAD_S64 ] = &&LABEL_FILTER_OP_LOAD_S64, + [ FILTER_OP_LOAD_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_DOUBLE, + + /* cast */ + [ FILTER_OP_CAST_TO_S64 ] = &&LABEL_FILTER_OP_CAST_TO_S64, + [ FILTER_OP_CAST_DOUBLE_TO_S64 ] = &&LABEL_FILTER_OP_CAST_DOUBLE_TO_S64, + [ FILTER_OP_CAST_NOP ] = &&LABEL_FILTER_OP_CAST_NOP, + }; +#endif /* #ifndef INTERPRETER_USE_SWITCH */ + + START_OP + + OP(FILTER_OP_UNKNOWN): + OP(FILTER_OP_LOAD_FIELD_REF): +#ifdef INTERPRETER_USE_SWITCH + default: +#endif /* INTERPRETER_USE_SWITCH */ + ERR("unknown bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + OP(FILTER_OP_RETURN): + retval = !!reg[0].v; + ret = 0; + goto end; + + /* binary */ + OP(FILTER_OP_MUL): + OP(FILTER_OP_DIV): + OP(FILTER_OP_MOD): + OP(FILTER_OP_PLUS): + OP(FILTER_OP_MINUS): + OP(FILTER_OP_RSHIFT): + OP(FILTER_OP_LSHIFT): + OP(FILTER_OP_BIN_AND): + OP(FILTER_OP_BIN_OR): + OP(FILTER_OP_BIN_XOR): + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + OP(FILTER_OP_EQ): + OP(FILTER_OP_NE): + OP(FILTER_OP_GT): + OP(FILTER_OP_LT): + OP(FILTER_OP_GE): + OP(FILTER_OP_LE): + ERR("unsupported non-specialized bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + OP(FILTER_OP_EQ_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, "==") == 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_NE_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, "!=") != 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GT_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, ">") > 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LT_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, "<") < 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GE_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, ">=") >= 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LE_STRING): + { + reg[REG_R0].v = (reg_strcmp(reg, "<=") <= 0); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + + OP(FILTER_OP_EQ_S64): + { + reg[REG_R0].v = (reg[REG_R0].v == reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_NE_S64): + { + reg[REG_R0].v = (reg[REG_R0].v != reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GT_S64): + { + reg[REG_R0].v = (reg[REG_R0].v > reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LT_S64): + { + reg[REG_R0].v = (reg[REG_R0].v < reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GE_S64): + { + reg[REG_R0].v = (reg[REG_R0].v >= reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LE_S64): + { + reg[REG_R0].v = (reg[REG_R0].v <= reg[REG_R1].v); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + + OP(FILTER_OP_EQ_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d == reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_NE_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d != reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GT_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d > reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LT_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d < reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_GE_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d >= reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + OP(FILTER_OP_LE_DOUBLE): + { + if (unlikely(reg[REG_R0].type == REG_S64)) + reg[REG_R0].d = (double) reg[REG_R0].v; + else if (unlikely(reg[REG_R1].type == REG_S64)) + reg[REG_R1].d = (double) reg[REG_R1].v; + reg[REG_R0].v = (reg[REG_R0].d <= reg[REG_R1].d); + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + PO; + } + + /* unary */ + OP(FILTER_OP_UNARY_PLUS): + OP(FILTER_OP_UNARY_MINUS): + OP(FILTER_OP_UNARY_NOT): + ERR("unsupported non-specialized bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + + OP(FILTER_OP_UNARY_PLUS_S64): + OP(FILTER_OP_UNARY_PLUS_DOUBLE): + { + next_pc += sizeof(struct unary_op); + PO; + } + OP(FILTER_OP_UNARY_MINUS_S64): + { + struct unary_op *insn = (struct unary_op *) pc; + + reg[insn->reg].v = -reg[insn->reg].v; + next_pc += sizeof(struct unary_op); + PO; + } + OP(FILTER_OP_UNARY_MINUS_DOUBLE): + { + struct unary_op *insn = (struct unary_op *) pc; + + reg[insn->reg].d = -reg[insn->reg].d; + next_pc += sizeof(struct unary_op); + PO; + } + OP(FILTER_OP_UNARY_NOT_S64): + { + struct unary_op *insn = (struct unary_op *) pc; + + reg[insn->reg].v = !reg[insn->reg].v; + next_pc += sizeof(struct unary_op); + PO; + } + OP(FILTER_OP_UNARY_NOT_DOUBLE): + { + struct unary_op *insn = (struct unary_op *) pc; + + reg[insn->reg].d = !reg[insn->reg].d; + next_pc += sizeof(struct unary_op); + PO; + } + + /* logical */ + OP(FILTER_OP_AND): + { + struct logical_op *insn = (struct logical_op *) pc; + + /* If REG_R0 is 0, skip and evaluate to 0 */ + if (unlikely(reg[REG_R0].v == 0)) { + dbg_printf("Jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + next_pc = start_pc + insn->skip_offset; + } else { + next_pc += sizeof(struct logical_op); + } + PO; + } + OP(FILTER_OP_OR): + { + struct logical_op *insn = (struct logical_op *) pc; + + /* If REG_R0 is nonzero, skip and evaluate to 1 */ + + if (unlikely(reg[REG_R0].v != 0)) { + reg[REG_R0].v = 1; + dbg_printf("Jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + next_pc = start_pc + insn->skip_offset; + } else { + next_pc += sizeof(struct logical_op); + } + PO; + } + + + /* load */ + OP(FILTER_OP_LOAD_FIELD_REF_STRING): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type string\n", + ref->offset); + reg[insn->reg].str = + *(const char * const *) &filter_stack_data[ref->offset]; + if (unlikely(!reg[insn->reg].str)) { + dbg_printf("Filter warning: loading a NULL string.\n"); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_STRING; + reg[insn->reg].seq_len = UINT_MAX; + reg[insn->reg].literal = 0; + dbg_printf("ref load string %s\n", reg[insn->reg].str); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + + OP(FILTER_OP_LOAD_FIELD_REF_SEQUENCE): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type sequence\n", + ref->offset); + reg[insn->reg].seq_len = + *(unsigned long *) &filter_stack_data[ref->offset]; + reg[insn->reg].str = + *(const char **) (&filter_stack_data[ref->offset + + sizeof(unsigned long)]); + if (unlikely(!reg[insn->reg].str)) { + dbg_printf("Filter warning: loading a NULL sequence.\n"); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + + OP(FILTER_OP_LOAD_FIELD_REF_S64): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type s64\n", + ref->offset); + memcpy(®[insn->reg].v, &filter_stack_data[ref->offset], + sizeof(struct literal_numeric)); + reg[insn->reg].type = REG_S64; + dbg_printf("ref load s64 %" PRIi64 "\n", reg[insn->reg].v); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + + OP(FILTER_OP_LOAD_FIELD_REF_DOUBLE): + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + dbg_printf("load field ref offset %u type double\n", + ref->offset); + memcpy(®[insn->reg].d, &filter_stack_data[ref->offset], + sizeof(struct literal_double)); + reg[insn->reg].type = REG_DOUBLE; + dbg_printf("ref load double %g\n", reg[insn->reg].d); + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + PO; + } + + OP(FILTER_OP_LOAD_STRING): + { + struct load_op *insn = (struct load_op *) pc; + + dbg_printf("load string %s\n", insn->data); + reg[insn->reg].str = insn->data; + reg[insn->reg].type = REG_STRING; + reg[insn->reg].seq_len = UINT_MAX; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + PO; + } + + OP(FILTER_OP_LOAD_S64): + { + struct load_op *insn = (struct load_op *) pc; + + memcpy(®[insn->reg].v, insn->data, + sizeof(struct literal_numeric)); + dbg_printf("load s64 %" PRIi64 "\n", reg[insn->reg].v); + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_numeric); + PO; + } + + OP(FILTER_OP_LOAD_DOUBLE): + { + struct load_op *insn = (struct load_op *) pc; + + memcpy(®[insn->reg].d, insn->data, + sizeof(struct literal_double)); + dbg_printf("load s64 %g\n", reg[insn->reg].d); + reg[insn->reg].type = REG_DOUBLE; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_double); + PO; + } + + /* cast */ + OP(FILTER_OP_CAST_TO_S64): + ERR("unsupported non-specialized bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + OP(FILTER_OP_CAST_DOUBLE_TO_S64): + { + struct cast_op *insn = (struct cast_op *) pc; + + reg[insn->reg].v = (int64_t) reg[insn->reg].d; + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct cast_op); + PO; + } + + OP(FILTER_OP_CAST_NOP): + { + next_pc += sizeof(struct cast_op); + PO; + } + + END_OP +end: + /* return 0 (discard) on error */ + if (ret) + return 0; + return retval; +} + +#undef START_OP +#undef OP +#undef PO +#undef END_OP diff --git a/liblttng-ust/lttng-filter-specialize.c b/liblttng-ust/lttng-filter-specialize.c new file mode 100644 index 00000000..fc350fd6 --- /dev/null +++ b/liblttng-ust/lttng-filter-specialize.c @@ -0,0 +1,455 @@ +/* + * lttng-filter-specialize.c + * + * LTTng UST filter code specializer. + * + * Copyright (C) 2010-2012 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. + * + * 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. + * + * 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 + */ + +#include "lttng-filter.h" + +int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) +{ + void *pc, *next_pc, *start_pc; + int ret = -EINVAL; + struct vreg reg[NR_REG]; + int i; + + for (i = 0; i < NR_REG; i++) { + reg[i].type = REG_TYPE_UNKNOWN; + reg[i].literal = 0; + } + + start_pc = &bytecode->data[0]; + for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; + pc = next_pc) { + switch (*(filter_opcode_t *) pc) { + case FILTER_OP_UNKNOWN: + default: + ERR("unknown bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_RETURN: + ret = 0; + goto end; + + /* binary */ + case FILTER_OP_MUL: + case FILTER_OP_DIV: + case FILTER_OP_MOD: + case FILTER_OP_PLUS: + case FILTER_OP_MINUS: + case FILTER_OP_RSHIFT: + case FILTER_OP_LSHIFT: + case FILTER_OP_BIN_AND: + case FILTER_OP_BIN_OR: + case FILTER_OP_BIN_XOR: + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_EQ: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_EQ_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_EQ_S64; + else + insn->op = FILTER_OP_EQ_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_EQ_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_NE: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_NE_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_NE_S64; + else + insn->op = FILTER_OP_NE_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_NE_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_GT: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_GT_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_GT_S64; + else + insn->op = FILTER_OP_GT_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_GT_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_LT: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_LT_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_LT_S64; + else + insn->op = FILTER_OP_LT_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_LT_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_GE: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_GE_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_GE_S64; + else + insn->op = FILTER_OP_GE_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_GE_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LE: + { + struct binary_op *insn = (struct binary_op *) pc; + + switch(reg[REG_R0].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + insn->op = FILTER_OP_LE_STRING; + break; + case REG_S64: + if (reg[REG_R1].type == REG_S64) + insn->op = FILTER_OP_LE_S64; + else + insn->op = FILTER_OP_LE_DOUBLE; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_LE_DOUBLE; + break; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_EQ_STRING: + case FILTER_OP_NE_STRING: + case FILTER_OP_GT_STRING: + case FILTER_OP_LT_STRING: + case FILTER_OP_GE_STRING: + case FILTER_OP_LE_STRING: + case FILTER_OP_EQ_S64: + case FILTER_OP_NE_S64: + case FILTER_OP_GT_S64: + case FILTER_OP_LT_S64: + case FILTER_OP_GE_S64: + case FILTER_OP_LE_S64: + case FILTER_OP_EQ_DOUBLE: + case FILTER_OP_NE_DOUBLE: + case FILTER_OP_GT_DOUBLE: + case FILTER_OP_LT_DOUBLE: + case FILTER_OP_GE_DOUBLE: + case FILTER_OP_LE_DOUBLE: + { + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + /* unary */ + case FILTER_OP_UNARY_PLUS: + { + struct unary_op *insn = (struct unary_op *) pc; + + switch(reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + insn->op = FILTER_OP_UNARY_PLUS_S64; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_UNARY_PLUS_DOUBLE; + break; + } + next_pc += sizeof(struct unary_op); + break; + } + + case FILTER_OP_UNARY_MINUS: + { + struct unary_op *insn = (struct unary_op *) pc; + + switch(reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + insn->op = FILTER_OP_UNARY_MINUS_S64; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_UNARY_MINUS_DOUBLE; + break; + } + next_pc += sizeof(struct unary_op); + break; + } + + case FILTER_OP_UNARY_NOT: + { + struct unary_op *insn = (struct unary_op *) pc; + + switch(reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_S64: + insn->op = FILTER_OP_UNARY_NOT_S64; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_UNARY_NOT_DOUBLE; + break; + } + next_pc += sizeof(struct unary_op); + break; + } + + case FILTER_OP_UNARY_PLUS_S64: + case FILTER_OP_UNARY_MINUS_S64: + case FILTER_OP_UNARY_NOT_S64: + case FILTER_OP_UNARY_PLUS_DOUBLE: + case FILTER_OP_UNARY_MINUS_DOUBLE: + case FILTER_OP_UNARY_NOT_DOUBLE: + { + next_pc += sizeof(struct unary_op); + break; + } + + /* logical */ + case FILTER_OP_AND: + case FILTER_OP_OR: + { + next_pc += sizeof(struct logical_op); + break; + } + + /* load */ + case FILTER_OP_LOAD_FIELD_REF: + { + ERR("Unknown field ref type\n"); + ret = -EINVAL; + goto end; + } + case FILTER_OP_LOAD_FIELD_REF_STRING: + case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + case FILTER_OP_LOAD_FIELD_REF_S64: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + case FILTER_OP_LOAD_FIELD_REF_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + case FILTER_OP_LOAD_STRING: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + break; + } + + case FILTER_OP_LOAD_S64: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_numeric); + break; + } + + case FILTER_OP_LOAD_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_double); + break; + } + + /* cast */ + case FILTER_OP_CAST_TO_S64: + { + struct cast_op *insn = (struct cast_op *) pc; + + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Cast op can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + insn->op = FILTER_OP_CAST_NOP; + break; + case REG_DOUBLE: + insn->op = FILTER_OP_CAST_DOUBLE_TO_S64; + break; + } + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct cast_op); + break; + } + case FILTER_OP_CAST_DOUBLE_TO_S64: + { + struct cast_op *insn = (struct cast_op *) pc; + + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct cast_op); + break; + } + case FILTER_OP_CAST_NOP: + { + next_pc += sizeof(struct cast_op); + break; + } + + + } + } +end: + return ret; +} diff --git a/liblttng-ust/lttng-filter-validator.c b/liblttng-ust/lttng-filter-validator.c new file mode 100644 index 00000000..6991931b --- /dev/null +++ b/liblttng-ust/lttng-filter-validator.c @@ -0,0 +1,671 @@ +/* + * lttng-filter-validator.c + * + * LTTng UST filter bytecode validator. + * + * Copyright (C) 2010-2012 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. + * + * 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. + * + * 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 + */ + +#include "lttng-filter.h" + +static +int bin_op_compare_check(struct vreg reg[NR_REG], const char *str) +{ + switch (reg[REG_R0].type) { + default: + goto error_unknown; + + case REG_STRING: + switch (reg[REG_R1].type) { + default: + goto error_unknown; + + case REG_STRING: + break; + case REG_S64: + case REG_DOUBLE: + goto error_mismatch; + } + break; + case REG_S64: + case REG_DOUBLE: + switch (reg[REG_R1].type) { + default: + goto error_unknown; + + case REG_STRING: + goto error_mismatch; + + case REG_S64: + case REG_DOUBLE: + break; + } + break; + } + return 0; + +error_unknown: + + return -EINVAL; +error_mismatch: + ERR("type mismatch for '%s' binary operator\n", str); + return -EINVAL; +} + +/* + * Validate bytecode range overflow within the validation pass. + * Called for each instruction encountered. + */ +static +int bytecode_validate_overflow(struct bytecode_runtime *bytecode, + void *start_pc, void *pc) +{ + int ret = 0; + + switch (*(filter_opcode_t *) pc) { + case FILTER_OP_UNKNOWN: + default: + { + ERR("unknown bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + break; + } + + case FILTER_OP_RETURN: + { + if (unlikely(pc + sizeof(struct return_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + /* binary */ + case FILTER_OP_MUL: + case FILTER_OP_DIV: + case FILTER_OP_MOD: + case FILTER_OP_PLUS: + case FILTER_OP_MINUS: + case FILTER_OP_RSHIFT: + case FILTER_OP_LSHIFT: + case FILTER_OP_BIN_AND: + case FILTER_OP_BIN_OR: + case FILTER_OP_BIN_XOR: + { + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + break; + } + + case FILTER_OP_EQ: + case FILTER_OP_NE: + case FILTER_OP_GT: + case FILTER_OP_LT: + case FILTER_OP_GE: + case FILTER_OP_LE: + case FILTER_OP_EQ_STRING: + case FILTER_OP_NE_STRING: + case FILTER_OP_GT_STRING: + case FILTER_OP_LT_STRING: + case FILTER_OP_GE_STRING: + case FILTER_OP_LE_STRING: + case FILTER_OP_EQ_S64: + case FILTER_OP_NE_S64: + case FILTER_OP_GT_S64: + case FILTER_OP_LT_S64: + case FILTER_OP_GE_S64: + case FILTER_OP_LE_S64: + case FILTER_OP_EQ_DOUBLE: + case FILTER_OP_NE_DOUBLE: + case FILTER_OP_GT_DOUBLE: + case FILTER_OP_LT_DOUBLE: + case FILTER_OP_GE_DOUBLE: + case FILTER_OP_LE_DOUBLE: + { + if (unlikely(pc + sizeof(struct binary_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + /* unary */ + case FILTER_OP_UNARY_PLUS: + case FILTER_OP_UNARY_MINUS: + case FILTER_OP_UNARY_NOT: + case FILTER_OP_UNARY_PLUS_S64: + case FILTER_OP_UNARY_MINUS_S64: + case FILTER_OP_UNARY_NOT_S64: + case FILTER_OP_UNARY_PLUS_DOUBLE: + case FILTER_OP_UNARY_MINUS_DOUBLE: + case FILTER_OP_UNARY_NOT_DOUBLE: + { + if (unlikely(pc + sizeof(struct unary_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + /* logical */ + case FILTER_OP_AND: + case FILTER_OP_OR: + { + if (unlikely(pc + sizeof(struct logical_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + /* load */ + case FILTER_OP_LOAD_FIELD_REF: + { + ERR("Unknown field ref type\n"); + ret = -EINVAL; + break; + } + case FILTER_OP_LOAD_FIELD_REF_STRING: + case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: + case FILTER_OP_LOAD_FIELD_REF_S64: + case FILTER_OP_LOAD_FIELD_REF_DOUBLE: + { + if (unlikely(pc + sizeof(struct load_op) + sizeof(struct field_ref) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + case FILTER_OP_LOAD_STRING: + { + struct load_op *insn = (struct load_op *) pc; + uint32_t str_len, maxlen; + + if (unlikely(pc + sizeof(struct load_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + break; + } + + maxlen = start_pc + bytecode->len - pc - sizeof(struct load_op); + str_len = strnlen(insn->data, maxlen); + if (unlikely(str_len >= maxlen)) { + /* Final '\0' not found within range */ + ret = -EINVAL; + } + break; + } + + case FILTER_OP_LOAD_S64: + { + if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_numeric) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + case FILTER_OP_LOAD_DOUBLE: + { + if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_double) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + + case FILTER_OP_CAST_TO_S64: + case FILTER_OP_CAST_DOUBLE_TO_S64: + case FILTER_OP_CAST_NOP: + { + if (unlikely(pc + sizeof(struct cast_op) + > start_pc + bytecode->len)) { + ret = -EINVAL; + } + break; + } + } + + return ret; +} + +int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode) +{ + void *pc, *next_pc, *start_pc; + int ret = -EINVAL; + struct vreg reg[NR_REG]; + int i; + + for (i = 0; i < NR_REG; i++) { + reg[i].type = REG_TYPE_UNKNOWN; + reg[i].literal = 0; + } + + start_pc = &bytecode->data[0]; + for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; + pc = next_pc) { + if (bytecode_validate_overflow(bytecode, start_pc, pc) != 0) { + ERR("filter bytecode overflow\n"); + ret = -EINVAL; + goto end; + } + dbg_printf("Validating op %s (%u)\n", + print_op((unsigned int) *(filter_opcode_t *) pc), + (unsigned int) *(filter_opcode_t *) pc); + switch (*(filter_opcode_t *) pc) { + case FILTER_OP_UNKNOWN: + default: + ERR("unknown bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_RETURN: + ret = 0; + goto end; + + /* binary */ + case FILTER_OP_MUL: + case FILTER_OP_DIV: + case FILTER_OP_MOD: + case FILTER_OP_PLUS: + case FILTER_OP_MINUS: + case FILTER_OP_RSHIFT: + case FILTER_OP_LSHIFT: + case FILTER_OP_BIN_AND: + case FILTER_OP_BIN_OR: + case FILTER_OP_BIN_XOR: + ERR("unsupported bytecode op %u\n", + (unsigned int) *(filter_opcode_t *) pc); + ret = -EINVAL; + goto end; + + case FILTER_OP_EQ: + { + ret = bin_op_compare_check(reg, "=="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_NE: + { + ret = bin_op_compare_check(reg, "!="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GT: + { + ret = bin_op_compare_check(reg, ">"); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LT: + { + ret = bin_op_compare_check(reg, "<"); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_GE: + { + ret = bin_op_compare_check(reg, ">="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + case FILTER_OP_LE: + { + ret = bin_op_compare_check(reg, "<="); + if (ret) + goto end; + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_EQ_STRING: + case FILTER_OP_NE_STRING: + case FILTER_OP_GT_STRING: + case FILTER_OP_LT_STRING: + case FILTER_OP_GE_STRING: + case FILTER_OP_LE_STRING: + { + if (reg[REG_R0].type != REG_STRING + || reg[REG_R1].type != REG_STRING) { + ERR("Unexpected register type for string comparator\n"); + ret = -EINVAL; + goto end; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_EQ_S64: + case FILTER_OP_NE_S64: + case FILTER_OP_GT_S64: + case FILTER_OP_LT_S64: + case FILTER_OP_GE_S64: + case FILTER_OP_LE_S64: + { + if (reg[REG_R0].type != REG_S64 + || reg[REG_R1].type != REG_S64) { + ERR("Unexpected register type for s64 comparator\n"); + ret = -EINVAL; + goto end; + } + reg[REG_R0].type = REG_S64; + next_pc += sizeof(struct binary_op); + break; + } + + case FILTER_OP_EQ_DOUBLE: + case FILTER_OP_NE_DOUBLE: + case FILTER_OP_GT_DOUBLE: + case FILTER_OP_LT_DOUBLE: + case FILTER_OP_GE_DOUBLE: + case FILTER_OP_LE_DOUBLE: + { + if ((reg[REG_R0].type != REG_DOUBLE && reg[REG_R0].type != REG_S64) + || (reg[REG_R1].type != REG_DOUBLE && reg[REG_R1].type != REG_S64)) { + ERR("Unexpected register type for double comparator\n"); + ret = -EINVAL; + goto end; + } + if (reg[REG_R0].type != REG_DOUBLE && reg[REG_R1].type != REG_DOUBLE) { + ERR("Double operator should have at least one double register\n"); + ret = -EINVAL; + goto end; + } + reg[REG_R0].type = REG_DOUBLE; + next_pc += sizeof(struct binary_op); + break; + } + + /* unary */ + case FILTER_OP_UNARY_PLUS: + case FILTER_OP_UNARY_MINUS: + case FILTER_OP_UNARY_NOT: + { + struct unary_op *insn = (struct unary_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Unary op can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + break; + case REG_DOUBLE: + break; + } + next_pc += sizeof(struct unary_op); + break; + } + + case FILTER_OP_UNARY_PLUS_S64: + case FILTER_OP_UNARY_MINUS_S64: + case FILTER_OP_UNARY_NOT_S64: + { + struct unary_op *insn = (struct unary_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + if (reg[insn->reg].type != REG_S64) { + ERR("Invalid register type\n"); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct unary_op); + break; + } + + case FILTER_OP_UNARY_PLUS_DOUBLE: + case FILTER_OP_UNARY_MINUS_DOUBLE: + case FILTER_OP_UNARY_NOT_DOUBLE: + { + struct unary_op *insn = (struct unary_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + if (reg[insn->reg].type != REG_DOUBLE) { + ERR("Invalid register type\n"); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct unary_op); + break; + } + + /* logical */ + case FILTER_OP_AND: + case FILTER_OP_OR: + { + struct logical_op *insn = (struct logical_op *) pc; + + if (reg[REG_R0].type != REG_S64) { + ERR("Logical comparator expects S64 register\n"); + ret = -EINVAL; + goto end; + } + + dbg_printf("Validate jumping to bytecode offset %u\n", + (unsigned int) insn->skip_offset); + if (unlikely(start_pc + insn->skip_offset <= pc)) { + ERR("Loops are not allowed in bytecode\n"); + ret = -EINVAL; + goto end; + } + next_pc += sizeof(struct logical_op); + break; + } + + /* load */ + case FILTER_OP_LOAD_FIELD_REF: + { + ERR("Unknown field ref type\n"); + ret = -EINVAL; + goto end; + } + case FILTER_OP_LOAD_FIELD_REF_STRING: + case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type string\n", + ref->offset); + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + case FILTER_OP_LOAD_FIELD_REF_S64: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type s64\n", + ref->offset); + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + case FILTER_OP_LOAD_FIELD_REF_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + struct field_ref *ref = (struct field_ref *) insn->data; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + dbg_printf("Validate load field ref offset %u type double\n", + ref->offset); + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 0; + next_pc += sizeof(struct load_op) + sizeof(struct field_ref); + break; + } + + case FILTER_OP_LOAD_STRING: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_STRING; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; + break; + } + + case FILTER_OP_LOAD_S64: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_S64; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_numeric); + break; + } + + case FILTER_OP_LOAD_DOUBLE: + { + struct load_op *insn = (struct load_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + reg[insn->reg].type = REG_DOUBLE; + reg[insn->reg].literal = 1; + next_pc += sizeof(struct load_op) + + sizeof(struct literal_double); + break; + } + + case FILTER_OP_CAST_TO_S64: + case FILTER_OP_CAST_DOUBLE_TO_S64: + { + struct cast_op *insn = (struct cast_op *) pc; + + if (unlikely(insn->reg >= REG_ERROR)) { + ERR("invalid register %u\n", + (unsigned int) insn->reg); + ret = -EINVAL; + goto end; + } + switch (reg[insn->reg].type) { + default: + ERR("unknown register type\n"); + ret = -EINVAL; + goto end; + + case REG_STRING: + ERR("Cast op can only be applied to numeric or floating point registers\n"); + ret = -EINVAL; + goto end; + case REG_S64: + break; + case REG_DOUBLE: + break; + } + if (insn->op == FILTER_OP_CAST_DOUBLE_TO_S64) { + if (reg[insn->reg].type != REG_DOUBLE) { + ERR("Cast expects double\n"); + ret = -EINVAL; + goto end; + } + } + reg[insn->reg].type = REG_S64; + next_pc += sizeof(struct cast_op); + break; + } + case FILTER_OP_CAST_NOP: + { + next_pc += sizeof(struct cast_op); + break; + } + + } + } +end: + return ret; +} diff --git a/liblttng-ust/lttng-filter.c b/liblttng-ust/lttng-filter.c index fa306f07..d5b2e207 100644 --- a/liblttng-ust/lttng-filter.c +++ b/liblttng-ust/lttng-filter.c @@ -20,73 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "filter-bytecode.h" - -#define NR_REG 2 - -#ifndef min_t -#define min_t(type, a, b) \ - ((type) (a) < (type) (b) ? (type) (a) : (type) (b)) -#endif - -#ifndef likely -#define likely(x) __builtin_expect(!!(x), 1) -#endif - -#ifndef unlikely -#define unlikely(x) __builtin_expect(!!(x), 0) -#endif - -#ifdef DEBUG -#define dbg_printf(fmt, args...) printf("[debug bytecode] " fmt, ## args) -#else -#define dbg_printf(fmt, args...) \ -do { \ - /* do nothing but check printf format */ \ - if (0) \ - printf("[debug bytecode] " fmt, ## args); \ -} while (0) -#endif - -/* Linked bytecode */ -struct bytecode_runtime { - uint16_t len; - char data[0]; -}; - -enum reg_type { - REG_S64, - REG_DOUBLE, - REG_STRING, - REG_TYPE_UNKNOWN, -}; - -/* Validation registers */ -struct vreg { - enum reg_type type; - int literal; /* is string literal ? */ -}; - -/* Execution registers */ -struct reg { - enum reg_type type; - int64_t v; - double d; - - const char *str; - size_t seq_len; - int literal; /* is string literal ? */ -}; +#include "lttng-filter.h" static const char *opnames[] = { [ FILTER_OP_UNKNOWN ] = "UNKNOWN", @@ -170,7 +104,6 @@ static const char *opnames[] = { [ FILTER_OP_CAST_NOP ] = "CAST_NOP", }; -static const char *print_op(enum filter_op op) { if (op >= NR_FILTER_OPS) @@ -179,1746 +112,6 @@ const char *print_op(enum filter_op op) return opnames[op]; } -/* - * -1: wildcard found. - * -2: unknown escape char. - * 0: normal char. - */ - -static -int parse_char(const char **p) -{ - switch (**p) { - case '\\': - (*p)++; - switch (**p) { - case '\\': - case '*': - return 0; - default: - return -2; - } - case '*': - return -1; - default: - return 0; - } -} - -static -int reg_strcmp(struct reg reg[NR_REG], const char *cmp_type) -{ - const char *p = reg[REG_R0].str, *q = reg[REG_R1].str; - int ret; - int diff; - - for (;;) { - int escaped_r0 = 0; - - if (unlikely(p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0')) { - if (q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0') - diff = 0; - else - diff = -1; - break; - } - if (unlikely(q - reg[REG_R1].str > reg[REG_R1].seq_len || *q == '\0')) { - if (p - reg[REG_R0].str > reg[REG_R0].seq_len || *p == '\0') - diff = 0; - else - diff = 1; - break; - } - if (reg[REG_R0].literal) { - ret = parse_char(&p); - if (ret == -1) { - return 0; - } else if (ret == -2) { - escaped_r0 = 1; - } - /* else compare both char */ - } - if (reg[REG_R1].literal) { - ret = parse_char(&q); - if (ret == -1) { - return 0; - } else if (ret == -2) { - if (!escaped_r0) - return -1; - } else { - if (escaped_r0) - return 1; - } - } else { - if (escaped_r0) - return 1; - } - diff = *p - *q; - if (diff != 0) - break; - p++; - q++; - } - return diff; -} - -static -int lttng_filter_false(void *filter_data, - const char *filter_stack_data) -{ - return 0; -} - -#ifdef INTERPRETER_USE_SWITCH - -/* - * Fallback for compilers that do not support taking address of labels. - */ - -#define START_OP \ - start_pc = &bytecode->data[0]; \ - for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; \ - pc = next_pc) { \ - dbg_printf("Executing op %s (%u)\n", \ - print_op((unsigned int) *(filter_opcode_t *) pc), \ - (unsigned int) *(filter_opcode_t *) pc); \ - switch (*(filter_opcode_t *) pc) { - -#define OP(name) case name - -#define PO break - -#define END_OP } \ - } - -#else - -/* - * Dispatch-table based interpreter. - */ - -#define START_OP \ - start_pc = &bytecode->data[0]; \ - pc = next_pc = start_pc; \ - if (unlikely(pc - start_pc >= bytecode->len)) \ - goto end; \ - goto *dispatch[*(filter_opcode_t *) pc]; - -#define OP(name) \ -LABEL_##name - -#define PO \ - pc = next_pc; \ - goto *dispatch[*(filter_opcode_t *) pc]; - -#define END_OP - -#endif - -static -int lttng_filter_interpret_bytecode(void *filter_data, - const char *filter_stack_data) -{ - struct bytecode_runtime *bytecode = filter_data; - void *pc, *next_pc, *start_pc; - int ret = -EINVAL; - int retval = 0; - struct reg reg[NR_REG]; -#ifndef INTERPRETER_USE_SWITCH - static void *dispatch[NR_FILTER_OPS] = { - [ FILTER_OP_UNKNOWN ] = &&LABEL_FILTER_OP_UNKNOWN, - - [ FILTER_OP_RETURN ] = &&LABEL_FILTER_OP_RETURN, - - /* binary */ - [ FILTER_OP_MUL ] = &&LABEL_FILTER_OP_MUL, - [ FILTER_OP_DIV ] = &&LABEL_FILTER_OP_DIV, - [ FILTER_OP_MOD ] = &&LABEL_FILTER_OP_MOD, - [ FILTER_OP_PLUS ] = &&LABEL_FILTER_OP_PLUS, - [ FILTER_OP_MINUS ] = &&LABEL_FILTER_OP_MINUS, - [ FILTER_OP_RSHIFT ] = &&LABEL_FILTER_OP_RSHIFT, - [ FILTER_OP_LSHIFT ] = &&LABEL_FILTER_OP_LSHIFT, - [ FILTER_OP_BIN_AND ] = &&LABEL_FILTER_OP_BIN_AND, - [ FILTER_OP_BIN_OR ] = &&LABEL_FILTER_OP_BIN_OR, - [ FILTER_OP_BIN_XOR ] = &&LABEL_FILTER_OP_BIN_XOR, - - /* binary comparators */ - [ FILTER_OP_EQ ] = &&LABEL_FILTER_OP_EQ, - [ FILTER_OP_NE ] = &&LABEL_FILTER_OP_NE, - [ FILTER_OP_GT ] = &&LABEL_FILTER_OP_GT, - [ FILTER_OP_LT ] = &&LABEL_FILTER_OP_LT, - [ FILTER_OP_GE ] = &&LABEL_FILTER_OP_GE, - [ FILTER_OP_LE ] = &&LABEL_FILTER_OP_LE, - - /* string binary comparator */ - [ FILTER_OP_EQ_STRING ] = &&LABEL_FILTER_OP_EQ_STRING, - [ FILTER_OP_NE_STRING ] = &&LABEL_FILTER_OP_NE_STRING, - [ FILTER_OP_GT_STRING ] = &&LABEL_FILTER_OP_GT_STRING, - [ FILTER_OP_LT_STRING ] = &&LABEL_FILTER_OP_LT_STRING, - [ FILTER_OP_GE_STRING ] = &&LABEL_FILTER_OP_GE_STRING, - [ FILTER_OP_LE_STRING ] = &&LABEL_FILTER_OP_LE_STRING, - - /* s64 binary comparator */ - [ FILTER_OP_EQ_S64 ] = &&LABEL_FILTER_OP_EQ_S64, - [ FILTER_OP_NE_S64 ] = &&LABEL_FILTER_OP_NE_S64, - [ FILTER_OP_GT_S64 ] = &&LABEL_FILTER_OP_GT_S64, - [ FILTER_OP_LT_S64 ] = &&LABEL_FILTER_OP_LT_S64, - [ FILTER_OP_GE_S64 ] = &&LABEL_FILTER_OP_GE_S64, - [ FILTER_OP_LE_S64 ] = &&LABEL_FILTER_OP_LE_S64, - - /* double binary comparator */ - [ FILTER_OP_EQ_DOUBLE ] = &&LABEL_FILTER_OP_EQ_DOUBLE, - [ FILTER_OP_NE_DOUBLE ] = &&LABEL_FILTER_OP_NE_DOUBLE, - [ FILTER_OP_GT_DOUBLE ] = &&LABEL_FILTER_OP_GT_DOUBLE, - [ FILTER_OP_LT_DOUBLE ] = &&LABEL_FILTER_OP_LT_DOUBLE, - [ FILTER_OP_GE_DOUBLE ] = &&LABEL_FILTER_OP_GE_DOUBLE, - [ FILTER_OP_LE_DOUBLE ] = &&LABEL_FILTER_OP_LE_DOUBLE, - - /* unary */ - [ FILTER_OP_UNARY_PLUS ] = &&LABEL_FILTER_OP_UNARY_PLUS, - [ FILTER_OP_UNARY_MINUS ] = &&LABEL_FILTER_OP_UNARY_MINUS, - [ FILTER_OP_UNARY_NOT ] = &&LABEL_FILTER_OP_UNARY_NOT, - [ FILTER_OP_UNARY_PLUS_S64 ] = &&LABEL_FILTER_OP_UNARY_PLUS_S64, - [ FILTER_OP_UNARY_MINUS_S64 ] = &&LABEL_FILTER_OP_UNARY_MINUS_S64, - [ FILTER_OP_UNARY_NOT_S64 ] = &&LABEL_FILTER_OP_UNARY_NOT_S64, - [ FILTER_OP_UNARY_PLUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_PLUS_DOUBLE, - [ FILTER_OP_UNARY_MINUS_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_MINUS_DOUBLE, - [ FILTER_OP_UNARY_NOT_DOUBLE ] = &&LABEL_FILTER_OP_UNARY_NOT_DOUBLE, - - /* logical */ - [ FILTER_OP_AND ] = &&LABEL_FILTER_OP_AND, - [ FILTER_OP_OR ] = &&LABEL_FILTER_OP_OR, - - /* load */ - [ FILTER_OP_LOAD_FIELD_REF ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF, - [ FILTER_OP_LOAD_FIELD_REF_STRING ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_STRING, - [ FILTER_OP_LOAD_FIELD_REF_SEQUENCE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_SEQUENCE, - [ FILTER_OP_LOAD_FIELD_REF_S64 ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_S64, - [ FILTER_OP_LOAD_FIELD_REF_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_FIELD_REF_DOUBLE, - - [ FILTER_OP_LOAD_STRING ] = &&LABEL_FILTER_OP_LOAD_STRING, - [ FILTER_OP_LOAD_S64 ] = &&LABEL_FILTER_OP_LOAD_S64, - [ FILTER_OP_LOAD_DOUBLE ] = &&LABEL_FILTER_OP_LOAD_DOUBLE, - - /* cast */ - [ FILTER_OP_CAST_TO_S64 ] = &&LABEL_FILTER_OP_CAST_TO_S64, - [ FILTER_OP_CAST_DOUBLE_TO_S64 ] = &&LABEL_FILTER_OP_CAST_DOUBLE_TO_S64, - [ FILTER_OP_CAST_NOP ] = &&LABEL_FILTER_OP_CAST_NOP, - }; -#endif /* #ifndef INTERPRETER_USE_SWITCH */ - - START_OP - - OP(FILTER_OP_UNKNOWN): - OP(FILTER_OP_LOAD_FIELD_REF): -#ifdef INTERPRETER_USE_SWITCH - default: -#endif /* INTERPRETER_USE_SWITCH */ - ERR("unknown bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - OP(FILTER_OP_RETURN): - retval = !!reg[0].v; - ret = 0; - goto end; - - /* binary */ - OP(FILTER_OP_MUL): - OP(FILTER_OP_DIV): - OP(FILTER_OP_MOD): - OP(FILTER_OP_PLUS): - OP(FILTER_OP_MINUS): - OP(FILTER_OP_RSHIFT): - OP(FILTER_OP_LSHIFT): - OP(FILTER_OP_BIN_AND): - OP(FILTER_OP_BIN_OR): - OP(FILTER_OP_BIN_XOR): - ERR("unsupported bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - OP(FILTER_OP_EQ): - OP(FILTER_OP_NE): - OP(FILTER_OP_GT): - OP(FILTER_OP_LT): - OP(FILTER_OP_GE): - OP(FILTER_OP_LE): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - OP(FILTER_OP_EQ_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, "==") == 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_NE_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, "!=") != 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GT_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, ">") > 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LT_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, "<") < 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GE_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, ">=") >= 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LE_STRING): - { - reg[REG_R0].v = (reg_strcmp(reg, "<=") <= 0); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - - OP(FILTER_OP_EQ_S64): - { - reg[REG_R0].v = (reg[REG_R0].v == reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_NE_S64): - { - reg[REG_R0].v = (reg[REG_R0].v != reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GT_S64): - { - reg[REG_R0].v = (reg[REG_R0].v > reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LT_S64): - { - reg[REG_R0].v = (reg[REG_R0].v < reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GE_S64): - { - reg[REG_R0].v = (reg[REG_R0].v >= reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LE_S64): - { - reg[REG_R0].v = (reg[REG_R0].v <= reg[REG_R1].v); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - - OP(FILTER_OP_EQ_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d == reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_NE_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d != reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GT_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d > reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LT_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d < reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_GE_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d >= reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - OP(FILTER_OP_LE_DOUBLE): - { - if (unlikely(reg[REG_R0].type == REG_S64)) - reg[REG_R0].d = (double) reg[REG_R0].v; - else if (unlikely(reg[REG_R1].type == REG_S64)) - reg[REG_R1].d = (double) reg[REG_R1].v; - reg[REG_R0].v = (reg[REG_R0].d <= reg[REG_R1].d); - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - PO; - } - - /* unary */ - OP(FILTER_OP_UNARY_PLUS): - OP(FILTER_OP_UNARY_MINUS): - OP(FILTER_OP_UNARY_NOT): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - - OP(FILTER_OP_UNARY_PLUS_S64): - OP(FILTER_OP_UNARY_PLUS_DOUBLE): - { - next_pc += sizeof(struct unary_op); - PO; - } - OP(FILTER_OP_UNARY_MINUS_S64): - { - struct unary_op *insn = (struct unary_op *) pc; - - reg[insn->reg].v = -reg[insn->reg].v; - next_pc += sizeof(struct unary_op); - PO; - } - OP(FILTER_OP_UNARY_MINUS_DOUBLE): - { - struct unary_op *insn = (struct unary_op *) pc; - - reg[insn->reg].d = -reg[insn->reg].d; - next_pc += sizeof(struct unary_op); - PO; - } - OP(FILTER_OP_UNARY_NOT_S64): - { - struct unary_op *insn = (struct unary_op *) pc; - - reg[insn->reg].v = !reg[insn->reg].v; - next_pc += sizeof(struct unary_op); - PO; - } - OP(FILTER_OP_UNARY_NOT_DOUBLE): - { - struct unary_op *insn = (struct unary_op *) pc; - - reg[insn->reg].d = !reg[insn->reg].d; - next_pc += sizeof(struct unary_op); - PO; - } - - /* logical */ - OP(FILTER_OP_AND): - { - struct logical_op *insn = (struct logical_op *) pc; - - /* If REG_R0 is 0, skip and evaluate to 0 */ - if (unlikely(reg[REG_R0].v == 0)) { - dbg_printf("Jumping to bytecode offset %u\n", - (unsigned int) insn->skip_offset); - next_pc = start_pc + insn->skip_offset; - } else { - next_pc += sizeof(struct logical_op); - } - PO; - } - OP(FILTER_OP_OR): - { - struct logical_op *insn = (struct logical_op *) pc; - - /* If REG_R0 is nonzero, skip and evaluate to 1 */ - - if (unlikely(reg[REG_R0].v != 0)) { - reg[REG_R0].v = 1; - dbg_printf("Jumping to bytecode offset %u\n", - (unsigned int) insn->skip_offset); - next_pc = start_pc + insn->skip_offset; - } else { - next_pc += sizeof(struct logical_op); - } - PO; - } - - - /* load */ - OP(FILTER_OP_LOAD_FIELD_REF_STRING): - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - dbg_printf("load field ref offset %u type string\n", - ref->offset); - reg[insn->reg].str = - *(const char * const *) &filter_stack_data[ref->offset]; - if (unlikely(!reg[insn->reg].str)) { - dbg_printf("Filter warning: loading a NULL string.\n"); - ret = -EINVAL; - goto end; - } - reg[insn->reg].type = REG_STRING; - reg[insn->reg].seq_len = UINT_MAX; - reg[insn->reg].literal = 0; - dbg_printf("ref load string %s\n", reg[insn->reg].str); - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - PO; - } - - OP(FILTER_OP_LOAD_FIELD_REF_SEQUENCE): - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - dbg_printf("load field ref offset %u type sequence\n", - ref->offset); - reg[insn->reg].seq_len = - *(unsigned long *) &filter_stack_data[ref->offset]; - reg[insn->reg].str = - *(const char **) (&filter_stack_data[ref->offset - + sizeof(unsigned long)]); - if (unlikely(!reg[insn->reg].str)) { - dbg_printf("Filter warning: loading a NULL sequence.\n"); - ret = -EINVAL; - goto end; - } - reg[insn->reg].type = REG_STRING; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - PO; - } - - OP(FILTER_OP_LOAD_FIELD_REF_S64): - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - dbg_printf("load field ref offset %u type s64\n", - ref->offset); - memcpy(®[insn->reg].v, &filter_stack_data[ref->offset], - sizeof(struct literal_numeric)); - reg[insn->reg].type = REG_S64; - dbg_printf("ref load s64 %" PRIi64 "\n", reg[insn->reg].v); - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - PO; - } - - OP(FILTER_OP_LOAD_FIELD_REF_DOUBLE): - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - dbg_printf("load field ref offset %u type double\n", - ref->offset); - memcpy(®[insn->reg].d, &filter_stack_data[ref->offset], - sizeof(struct literal_double)); - reg[insn->reg].type = REG_DOUBLE; - dbg_printf("ref load double %g\n", reg[insn->reg].d); - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - PO; - } - - OP(FILTER_OP_LOAD_STRING): - { - struct load_op *insn = (struct load_op *) pc; - - dbg_printf("load string %s\n", insn->data); - reg[insn->reg].str = insn->data; - reg[insn->reg].type = REG_STRING; - reg[insn->reg].seq_len = UINT_MAX; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; - PO; - } - - OP(FILTER_OP_LOAD_S64): - { - struct load_op *insn = (struct load_op *) pc; - - memcpy(®[insn->reg].v, insn->data, - sizeof(struct literal_numeric)); - dbg_printf("load s64 %" PRIi64 "\n", reg[insn->reg].v); - reg[insn->reg].type = REG_S64; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_numeric); - PO; - } - - OP(FILTER_OP_LOAD_DOUBLE): - { - struct load_op *insn = (struct load_op *) pc; - - memcpy(®[insn->reg].d, insn->data, - sizeof(struct literal_double)); - dbg_printf("load s64 %g\n", reg[insn->reg].d); - reg[insn->reg].type = REG_DOUBLE; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_double); - PO; - } - - /* cast */ - OP(FILTER_OP_CAST_TO_S64): - ERR("unsupported non-specialized bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - OP(FILTER_OP_CAST_DOUBLE_TO_S64): - { - struct cast_op *insn = (struct cast_op *) pc; - - reg[insn->reg].v = (int64_t) reg[insn->reg].d; - reg[insn->reg].type = REG_S64; - next_pc += sizeof(struct cast_op); - PO; - } - - OP(FILTER_OP_CAST_NOP): - { - next_pc += sizeof(struct cast_op); - PO; - } - - END_OP -end: - /* return 0 (discard) on error */ - if (ret) - return 0; - return retval; -} - -#undef START_OP -#undef OP -#undef PO -#undef END_OP - -static -int bin_op_compare_check(struct vreg reg[NR_REG], const char *str) -{ - switch (reg[REG_R0].type) { - default: - goto error_unknown; - - case REG_STRING: - switch (reg[REG_R1].type) { - default: - goto error_unknown; - - case REG_STRING: - break; - case REG_S64: - case REG_DOUBLE: - goto error_mismatch; - } - break; - case REG_S64: - case REG_DOUBLE: - switch (reg[REG_R1].type) { - default: - goto error_unknown; - - case REG_STRING: - goto error_mismatch; - - case REG_S64: - case REG_DOUBLE: - break; - } - break; - } - return 0; - -error_unknown: - - return -EINVAL; -error_mismatch: - ERR("type mismatch for '%s' binary operator\n", str); - return -EINVAL; -} - -/* - * Validate bytecode range overflow within the validation pass. - * Called for each instruction encountered. - */ -static -int bytecode_validate_overflow(struct bytecode_runtime *bytecode, - void *start_pc, void *pc) -{ - int ret = 0; - - switch (*(filter_opcode_t *) pc) { - case FILTER_OP_UNKNOWN: - default: - { - ERR("unknown bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - break; - } - - case FILTER_OP_RETURN: - { - if (unlikely(pc + sizeof(struct return_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - /* binary */ - case FILTER_OP_MUL: - case FILTER_OP_DIV: - case FILTER_OP_MOD: - case FILTER_OP_PLUS: - case FILTER_OP_MINUS: - case FILTER_OP_RSHIFT: - case FILTER_OP_LSHIFT: - case FILTER_OP_BIN_AND: - case FILTER_OP_BIN_OR: - case FILTER_OP_BIN_XOR: - { - ERR("unsupported bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - break; - } - - case FILTER_OP_EQ: - case FILTER_OP_NE: - case FILTER_OP_GT: - case FILTER_OP_LT: - case FILTER_OP_GE: - case FILTER_OP_LE: - case FILTER_OP_EQ_STRING: - case FILTER_OP_NE_STRING: - case FILTER_OP_GT_STRING: - case FILTER_OP_LT_STRING: - case FILTER_OP_GE_STRING: - case FILTER_OP_LE_STRING: - case FILTER_OP_EQ_S64: - case FILTER_OP_NE_S64: - case FILTER_OP_GT_S64: - case FILTER_OP_LT_S64: - case FILTER_OP_GE_S64: - case FILTER_OP_LE_S64: - case FILTER_OP_EQ_DOUBLE: - case FILTER_OP_NE_DOUBLE: - case FILTER_OP_GT_DOUBLE: - case FILTER_OP_LT_DOUBLE: - case FILTER_OP_GE_DOUBLE: - case FILTER_OP_LE_DOUBLE: - { - if (unlikely(pc + sizeof(struct binary_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - /* unary */ - case FILTER_OP_UNARY_PLUS: - case FILTER_OP_UNARY_MINUS: - case FILTER_OP_UNARY_NOT: - case FILTER_OP_UNARY_PLUS_S64: - case FILTER_OP_UNARY_MINUS_S64: - case FILTER_OP_UNARY_NOT_S64: - case FILTER_OP_UNARY_PLUS_DOUBLE: - case FILTER_OP_UNARY_MINUS_DOUBLE: - case FILTER_OP_UNARY_NOT_DOUBLE: - { - if (unlikely(pc + sizeof(struct unary_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - /* logical */ - case FILTER_OP_AND: - case FILTER_OP_OR: - { - if (unlikely(pc + sizeof(struct logical_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - /* load */ - case FILTER_OP_LOAD_FIELD_REF: - { - ERR("Unknown field ref type\n"); - ret = -EINVAL; - break; - } - case FILTER_OP_LOAD_FIELD_REF_STRING: - case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: - case FILTER_OP_LOAD_FIELD_REF_S64: - case FILTER_OP_LOAD_FIELD_REF_DOUBLE: - { - if (unlikely(pc + sizeof(struct load_op) + sizeof(struct field_ref) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - case FILTER_OP_LOAD_STRING: - { - struct load_op *insn = (struct load_op *) pc; - uint32_t str_len, maxlen; - - if (unlikely(pc + sizeof(struct load_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - break; - } - - maxlen = start_pc + bytecode->len - pc - sizeof(struct load_op); - str_len = strnlen(insn->data, maxlen); - if (unlikely(str_len >= maxlen)) { - /* Final '\0' not found within range */ - ret = -EINVAL; - } - break; - } - - case FILTER_OP_LOAD_S64: - { - if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_numeric) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - case FILTER_OP_LOAD_DOUBLE: - { - if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_double) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - - case FILTER_OP_CAST_TO_S64: - case FILTER_OP_CAST_DOUBLE_TO_S64: - case FILTER_OP_CAST_NOP: - { - if (unlikely(pc + sizeof(struct cast_op) - > start_pc + bytecode->len)) { - ret = -EINVAL; - } - break; - } - } - - return ret; -} - -static -int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode) -{ - void *pc, *next_pc, *start_pc; - int ret = -EINVAL; - struct vreg reg[NR_REG]; - int i; - - for (i = 0; i < NR_REG; i++) { - reg[i].type = REG_TYPE_UNKNOWN; - reg[i].literal = 0; - } - - start_pc = &bytecode->data[0]; - for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; - pc = next_pc) { - if (bytecode_validate_overflow(bytecode, start_pc, pc) != 0) { - ERR("filter bytecode overflow\n"); - ret = -EINVAL; - goto end; - } - dbg_printf("Validating op %s (%u)\n", - print_op((unsigned int) *(filter_opcode_t *) pc), - (unsigned int) *(filter_opcode_t *) pc); - switch (*(filter_opcode_t *) pc) { - case FILTER_OP_UNKNOWN: - default: - ERR("unknown bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - case FILTER_OP_RETURN: - ret = 0; - goto end; - - /* binary */ - case FILTER_OP_MUL: - case FILTER_OP_DIV: - case FILTER_OP_MOD: - case FILTER_OP_PLUS: - case FILTER_OP_MINUS: - case FILTER_OP_RSHIFT: - case FILTER_OP_LSHIFT: - case FILTER_OP_BIN_AND: - case FILTER_OP_BIN_OR: - case FILTER_OP_BIN_XOR: - ERR("unsupported bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - case FILTER_OP_EQ: - { - ret = bin_op_compare_check(reg, "=="); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_NE: - { - ret = bin_op_compare_check(reg, "!="); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_GT: - { - ret = bin_op_compare_check(reg, ">"); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_LT: - { - ret = bin_op_compare_check(reg, "<"); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_GE: - { - ret = bin_op_compare_check(reg, ">="); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_LE: - { - ret = bin_op_compare_check(reg, "<="); - if (ret) - goto end; - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_EQ_STRING: - case FILTER_OP_NE_STRING: - case FILTER_OP_GT_STRING: - case FILTER_OP_LT_STRING: - case FILTER_OP_GE_STRING: - case FILTER_OP_LE_STRING: - { - if (reg[REG_R0].type != REG_STRING - || reg[REG_R1].type != REG_STRING) { - ERR("Unexpected register type for string comparator\n"); - ret = -EINVAL; - goto end; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_EQ_S64: - case FILTER_OP_NE_S64: - case FILTER_OP_GT_S64: - case FILTER_OP_LT_S64: - case FILTER_OP_GE_S64: - case FILTER_OP_LE_S64: - { - if (reg[REG_R0].type != REG_S64 - || reg[REG_R1].type != REG_S64) { - ERR("Unexpected register type for s64 comparator\n"); - ret = -EINVAL; - goto end; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_EQ_DOUBLE: - case FILTER_OP_NE_DOUBLE: - case FILTER_OP_GT_DOUBLE: - case FILTER_OP_LT_DOUBLE: - case FILTER_OP_GE_DOUBLE: - case FILTER_OP_LE_DOUBLE: - { - if ((reg[REG_R0].type != REG_DOUBLE && reg[REG_R0].type != REG_S64) - || (reg[REG_R1].type != REG_DOUBLE && reg[REG_R1].type != REG_S64)) { - ERR("Unexpected register type for double comparator\n"); - ret = -EINVAL; - goto end; - } - if (reg[REG_R0].type != REG_DOUBLE && reg[REG_R1].type != REG_DOUBLE) { - ERR("Double operator should have at least one double register\n"); - ret = -EINVAL; - goto end; - } - reg[REG_R0].type = REG_DOUBLE; - next_pc += sizeof(struct binary_op); - break; - } - - /* unary */ - case FILTER_OP_UNARY_PLUS: - case FILTER_OP_UNARY_MINUS: - case FILTER_OP_UNARY_NOT: - { - struct unary_op *insn = (struct unary_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - switch (reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - ERR("Unary op can only be applied to numeric or floating point registers\n"); - ret = -EINVAL; - goto end; - case REG_S64: - break; - case REG_DOUBLE: - break; - } - next_pc += sizeof(struct unary_op); - break; - } - - case FILTER_OP_UNARY_PLUS_S64: - case FILTER_OP_UNARY_MINUS_S64: - case FILTER_OP_UNARY_NOT_S64: - { - struct unary_op *insn = (struct unary_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - if (reg[insn->reg].type != REG_S64) { - ERR("Invalid register type\n"); - ret = -EINVAL; - goto end; - } - next_pc += sizeof(struct unary_op); - break; - } - - case FILTER_OP_UNARY_PLUS_DOUBLE: - case FILTER_OP_UNARY_MINUS_DOUBLE: - case FILTER_OP_UNARY_NOT_DOUBLE: - { - struct unary_op *insn = (struct unary_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - if (reg[insn->reg].type != REG_DOUBLE) { - ERR("Invalid register type\n"); - ret = -EINVAL; - goto end; - } - next_pc += sizeof(struct unary_op); - break; - } - - /* logical */ - case FILTER_OP_AND: - case FILTER_OP_OR: - { - struct logical_op *insn = (struct logical_op *) pc; - - if (reg[REG_R0].type != REG_S64) { - ERR("Logical comparator expects S64 register\n"); - ret = -EINVAL; - goto end; - } - - dbg_printf("Validate jumping to bytecode offset %u\n", - (unsigned int) insn->skip_offset); - if (unlikely(start_pc + insn->skip_offset <= pc)) { - ERR("Loops are not allowed in bytecode\n"); - ret = -EINVAL; - goto end; - } - next_pc += sizeof(struct logical_op); - break; - } - - /* load */ - case FILTER_OP_LOAD_FIELD_REF: - { - ERR("Unknown field ref type\n"); - ret = -EINVAL; - goto end; - } - case FILTER_OP_LOAD_FIELD_REF_STRING: - case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - dbg_printf("Validate load field ref offset %u type string\n", - ref->offset); - reg[insn->reg].type = REG_STRING; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - case FILTER_OP_LOAD_FIELD_REF_S64: - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - dbg_printf("Validate load field ref offset %u type s64\n", - ref->offset); - reg[insn->reg].type = REG_S64; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - case FILTER_OP_LOAD_FIELD_REF_DOUBLE: - { - struct load_op *insn = (struct load_op *) pc; - struct field_ref *ref = (struct field_ref *) insn->data; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - dbg_printf("Validate load field ref offset %u type double\n", - ref->offset); - reg[insn->reg].type = REG_DOUBLE; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - - case FILTER_OP_LOAD_STRING: - { - struct load_op *insn = (struct load_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - reg[insn->reg].type = REG_STRING; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; - break; - } - - case FILTER_OP_LOAD_S64: - { - struct load_op *insn = (struct load_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - reg[insn->reg].type = REG_S64; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_numeric); - break; - } - - case FILTER_OP_LOAD_DOUBLE: - { - struct load_op *insn = (struct load_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - reg[insn->reg].type = REG_DOUBLE; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_double); - break; - } - - case FILTER_OP_CAST_TO_S64: - case FILTER_OP_CAST_DOUBLE_TO_S64: - { - struct cast_op *insn = (struct cast_op *) pc; - - if (unlikely(insn->reg >= REG_ERROR)) { - ERR("invalid register %u\n", - (unsigned int) insn->reg); - ret = -EINVAL; - goto end; - } - switch (reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - ERR("Cast op can only be applied to numeric or floating point registers\n"); - ret = -EINVAL; - goto end; - case REG_S64: - break; - case REG_DOUBLE: - break; - } - if (insn->op == FILTER_OP_CAST_DOUBLE_TO_S64) { - if (reg[insn->reg].type != REG_DOUBLE) { - ERR("Cast expects double\n"); - ret = -EINVAL; - goto end; - } - } - reg[insn->reg].type = REG_S64; - next_pc += sizeof(struct cast_op); - break; - } - case FILTER_OP_CAST_NOP: - { - next_pc += sizeof(struct cast_op); - break; - } - - } - } -end: - return ret; -} - -static -int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode) -{ - void *pc, *next_pc, *start_pc; - int ret = -EINVAL; - struct vreg reg[NR_REG]; - int i; - - for (i = 0; i < NR_REG; i++) { - reg[i].type = REG_TYPE_UNKNOWN; - reg[i].literal = 0; - } - - start_pc = &bytecode->data[0]; - for (pc = next_pc = start_pc; pc - start_pc < bytecode->len; - pc = next_pc) { - switch (*(filter_opcode_t *) pc) { - case FILTER_OP_UNKNOWN: - default: - ERR("unknown bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - case FILTER_OP_RETURN: - ret = 0; - goto end; - - /* binary */ - case FILTER_OP_MUL: - case FILTER_OP_DIV: - case FILTER_OP_MOD: - case FILTER_OP_PLUS: - case FILTER_OP_MINUS: - case FILTER_OP_RSHIFT: - case FILTER_OP_LSHIFT: - case FILTER_OP_BIN_AND: - case FILTER_OP_BIN_OR: - case FILTER_OP_BIN_XOR: - ERR("unsupported bytecode op %u\n", - (unsigned int) *(filter_opcode_t *) pc); - ret = -EINVAL; - goto end; - - case FILTER_OP_EQ: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_EQ_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_EQ_S64; - else - insn->op = FILTER_OP_EQ_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_EQ_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_NE: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_NE_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_NE_S64; - else - insn->op = FILTER_OP_NE_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_NE_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_GT: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_GT_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_GT_S64; - else - insn->op = FILTER_OP_GT_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_GT_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_LT: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_LT_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_LT_S64; - else - insn->op = FILTER_OP_LT_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_LT_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_GE: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_GE_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_GE_S64; - else - insn->op = FILTER_OP_GE_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_GE_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - case FILTER_OP_LE: - { - struct binary_op *insn = (struct binary_op *) pc; - - switch(reg[REG_R0].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - insn->op = FILTER_OP_LE_STRING; - break; - case REG_S64: - if (reg[REG_R1].type == REG_S64) - insn->op = FILTER_OP_LE_S64; - else - insn->op = FILTER_OP_LE_DOUBLE; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_LE_DOUBLE; - break; - } - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - case FILTER_OP_EQ_STRING: - case FILTER_OP_NE_STRING: - case FILTER_OP_GT_STRING: - case FILTER_OP_LT_STRING: - case FILTER_OP_GE_STRING: - case FILTER_OP_LE_STRING: - case FILTER_OP_EQ_S64: - case FILTER_OP_NE_S64: - case FILTER_OP_GT_S64: - case FILTER_OP_LT_S64: - case FILTER_OP_GE_S64: - case FILTER_OP_LE_S64: - case FILTER_OP_EQ_DOUBLE: - case FILTER_OP_NE_DOUBLE: - case FILTER_OP_GT_DOUBLE: - case FILTER_OP_LT_DOUBLE: - case FILTER_OP_GE_DOUBLE: - case FILTER_OP_LE_DOUBLE: - { - reg[REG_R0].type = REG_S64; - next_pc += sizeof(struct binary_op); - break; - } - - /* unary */ - case FILTER_OP_UNARY_PLUS: - { - struct unary_op *insn = (struct unary_op *) pc; - - switch(reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_S64: - insn->op = FILTER_OP_UNARY_PLUS_S64; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_UNARY_PLUS_DOUBLE; - break; - } - next_pc += sizeof(struct unary_op); - break; - } - - case FILTER_OP_UNARY_MINUS: - { - struct unary_op *insn = (struct unary_op *) pc; - - switch(reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_S64: - insn->op = FILTER_OP_UNARY_MINUS_S64; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_UNARY_MINUS_DOUBLE; - break; - } - next_pc += sizeof(struct unary_op); - break; - } - - case FILTER_OP_UNARY_NOT: - { - struct unary_op *insn = (struct unary_op *) pc; - - switch(reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_S64: - insn->op = FILTER_OP_UNARY_NOT_S64; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_UNARY_NOT_DOUBLE; - break; - } - next_pc += sizeof(struct unary_op); - break; - } - - case FILTER_OP_UNARY_PLUS_S64: - case FILTER_OP_UNARY_MINUS_S64: - case FILTER_OP_UNARY_NOT_S64: - case FILTER_OP_UNARY_PLUS_DOUBLE: - case FILTER_OP_UNARY_MINUS_DOUBLE: - case FILTER_OP_UNARY_NOT_DOUBLE: - { - next_pc += sizeof(struct unary_op); - break; - } - - /* logical */ - case FILTER_OP_AND: - case FILTER_OP_OR: - { - next_pc += sizeof(struct logical_op); - break; - } - - /* load */ - case FILTER_OP_LOAD_FIELD_REF: - { - ERR("Unknown field ref type\n"); - ret = -EINVAL; - goto end; - } - case FILTER_OP_LOAD_FIELD_REF_STRING: - case FILTER_OP_LOAD_FIELD_REF_SEQUENCE: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_STRING; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - case FILTER_OP_LOAD_FIELD_REF_S64: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_S64; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - case FILTER_OP_LOAD_FIELD_REF_DOUBLE: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_DOUBLE; - reg[insn->reg].literal = 0; - next_pc += sizeof(struct load_op) + sizeof(struct field_ref); - break; - } - - case FILTER_OP_LOAD_STRING: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_STRING; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) + strlen(insn->data) + 1; - break; - } - - case FILTER_OP_LOAD_S64: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_S64; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_numeric); - break; - } - - case FILTER_OP_LOAD_DOUBLE: - { - struct load_op *insn = (struct load_op *) pc; - - reg[insn->reg].type = REG_DOUBLE; - reg[insn->reg].literal = 1; - next_pc += sizeof(struct load_op) - + sizeof(struct literal_double); - break; - } - - /* cast */ - case FILTER_OP_CAST_TO_S64: - { - struct cast_op *insn = (struct cast_op *) pc; - - switch (reg[insn->reg].type) { - default: - ERR("unknown register type\n"); - ret = -EINVAL; - goto end; - - case REG_STRING: - ERR("Cast op can only be applied to numeric or floating point registers\n"); - ret = -EINVAL; - goto end; - case REG_S64: - insn->op = FILTER_OP_CAST_NOP; - break; - case REG_DOUBLE: - insn->op = FILTER_OP_CAST_DOUBLE_TO_S64; - break; - } - reg[insn->reg].type = REG_S64; - next_pc += sizeof(struct cast_op); - break; - } - case FILTER_OP_CAST_DOUBLE_TO_S64: - { - struct cast_op *insn = (struct cast_op *) pc; - - reg[insn->reg].type = REG_S64; - next_pc += sizeof(struct cast_op); - break; - } - case FILTER_OP_CAST_NOP: - { - next_pc += sizeof(struct cast_op); - break; - } - - - } - } -end: - return ret; -} - static int apply_field_reloc(struct ltt_event *event, struct bytecode_runtime *runtime, diff --git a/liblttng-ust/lttng-filter.h b/liblttng-ust/lttng-filter.h new file mode 100644 index 00000000..4ef1c757 --- /dev/null +++ b/liblttng-ust/lttng-filter.h @@ -0,0 +1,104 @@ +#ifndef _LTTNG_FILTER_H +#define _LTTNG_FILTER_H + +/* + * lttng-filter.h + * + * LTTng UST filter header. + * + * Copyright (C) 2010-2012 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. + * + * 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. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "filter-bytecode.h" + +#define NR_REG 2 + +#ifndef min_t +#define min_t(type, a, b) \ + ((type) (a) < (type) (b) ? (type) (a) : (type) (b)) +#endif + +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif + +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#ifdef DEBUG +#define dbg_printf(fmt, args...) printf("[debug bytecode] " fmt, ## args) +#else +#define dbg_printf(fmt, args...) \ +do { \ + /* do nothing but check printf format */ \ + if (0) \ + printf("[debug bytecode] " fmt, ## args); \ +} while (0) +#endif + +/* Linked bytecode */ +struct bytecode_runtime { + uint16_t len; + char data[0]; +}; + +enum reg_type { + REG_S64, + REG_DOUBLE, + REG_STRING, + REG_TYPE_UNKNOWN, +}; + +/* Validation registers */ +struct vreg { + enum reg_type type; + int literal; /* is string literal ? */ +}; + +/* Execution registers */ +struct reg { + enum reg_type type; + int64_t v; + double d; + + const char *str; + size_t seq_len; + int literal; /* is string literal ? */ +}; + +const char *print_op(enum filter_op op); + +int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode); +int lttng_filter_specialize_bytecode(struct bytecode_runtime *bytecode); + +int lttng_filter_false(void *filter_data, + const char *filter_stack_data); +int lttng_filter_interpret_bytecode(void *filter_data, + const char *filter_stack_data); + +#endif /* _LTTNG_FILTER_H */ -- 2.34.1