X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust%2Flttng-filter-validator.c;h=456407ffde228fa625e3973b3d5e02c71b0bd4d7;hb=8c90a710949a214d87084ff67fc85f284bc93ef2;hp=239bda2f76ea931820d9f6a1361b4d65baf2ffe4;hpb=0305960f8200d1db7002b75d9a5c3ea37541e2c5;p=lttng-ust.git diff --git a/liblttng-ust/lttng-filter-validator.c b/liblttng-ust/lttng-filter-validator.c index 239bda2f..456407ff 100644 --- a/liblttng-ust/lttng-filter-validator.c +++ b/liblttng-ust/lttng-filter-validator.c @@ -64,13 +64,31 @@ int lttng_hash_match(struct cds_lfht_node *node, const void *key) } static -int merge_point_add(struct cds_lfht *ht, unsigned long target_pc, +int merge_points_compare(const struct vstack *stacka, + const struct vstack *stackb) +{ + int i, len; + + if (stacka->top != stackb->top) + return 1; + len = stacka->top + 1; + assert(len >= 0); + for (i = 0; i < len; i++) { + if (stacka->e[i].type != stackb->e[i].type) + return 1; + } + return 0; +} + +static +int merge_point_add_check(struct cds_lfht *ht, unsigned long target_pc, const struct vstack *stack) { struct lfht_mp_node *node; unsigned long hash = lttng_hash_mix((const void *) target_pc, sizeof(target_pc), lttng_hash_seed); + struct cds_lfht_node *ret; dbg_printf("Filter: adding merge point at offset %lu, hash %lu\n", target_pc, hash); @@ -79,7 +97,22 @@ int merge_point_add(struct cds_lfht *ht, unsigned long target_pc, return -ENOMEM; node->target_pc = target_pc; memcpy(&node->stack, stack, sizeof(node->stack)); - cds_lfht_add(ht, hash, &node->node); + ret = cds_lfht_add_unique(ht, hash, lttng_hash_match, + (const void *) target_pc, &node->node); + if (ret != &node->node) { + struct lfht_mp_node *ret_mp = + caa_container_of(ret, struct lfht_mp_node, node); + + /* Key already present */ + dbg_printf("Filter: compare merge points for offset %lu, hash %lu\n", + target_pc, hash); + free(node); + if (merge_points_compare(stack, &ret_mp->stack)) { + ERR("Merge points differ for offset %lu\n", + target_pc); + return -EINVAL; + } + } return 0; } @@ -157,7 +190,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct return_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -204,10 +237,22 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, case FILTER_OP_LT_DOUBLE: case FILTER_OP_GE_DOUBLE: case FILTER_OP_LE_DOUBLE: + case FILTER_OP_EQ_DOUBLE_S64: + case FILTER_OP_NE_DOUBLE_S64: + case FILTER_OP_GT_DOUBLE_S64: + case FILTER_OP_LT_DOUBLE_S64: + case FILTER_OP_GE_DOUBLE_S64: + case FILTER_OP_LE_DOUBLE_S64: + case FILTER_OP_EQ_S64_DOUBLE: + case FILTER_OP_NE_S64_DOUBLE: + case FILTER_OP_GT_S64_DOUBLE: + case FILTER_OP_LT_S64_DOUBLE: + case FILTER_OP_GE_S64_DOUBLE: + case FILTER_OP_LE_S64_DOUBLE: { if (unlikely(pc + sizeof(struct binary_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -225,7 +270,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct unary_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -236,7 +281,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct logical_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -255,7 +300,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct load_op) + sizeof(struct field_ref) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -267,7 +312,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, if (unlikely(pc + sizeof(struct load_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; break; } @@ -275,7 +320,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, str_len = strnlen(insn->data, maxlen); if (unlikely(str_len >= maxlen)) { /* Final '\0' not found within range */ - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -284,7 +329,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_numeric) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -293,7 +338,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct load_op) + sizeof(struct literal_double) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -304,7 +349,7 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode, { if (unlikely(pc + sizeof(struct cast_op) > start_pc + bytecode->len)) { - ret = -EINVAL; + ret = -ERANGE; } break; } @@ -475,14 +520,48 @@ int validate_instruction_context(struct bytecode_runtime *bytecode, ret = -EINVAL; goto end; } - if ((vstack_ax(stack)->type != REG_DOUBLE && vstack_ax(stack)->type != REG_S64) - || (vstack_bx(stack)-> type != REG_DOUBLE && vstack_bx(stack)->type != REG_S64)) { - ERR("Unexpected register type for double comparator\n"); + if (vstack_ax(stack)->type != REG_DOUBLE && vstack_bx(stack)->type != REG_DOUBLE) { + ERR("Double operator should have two double registers\n"); ret = -EINVAL; goto end; } - if (vstack_ax(stack)->type != REG_DOUBLE && vstack_bx(stack)->type != REG_DOUBLE) { - ERR("Double operator should have at least one double register\n"); + break; + } + + case FILTER_OP_EQ_DOUBLE_S64: + case FILTER_OP_NE_DOUBLE_S64: + case FILTER_OP_GT_DOUBLE_S64: + case FILTER_OP_LT_DOUBLE_S64: + case FILTER_OP_GE_DOUBLE_S64: + case FILTER_OP_LE_DOUBLE_S64: + { + if (!vstack_ax(stack) || !vstack_bx(stack)) { + ERR("Empty stack\n"); + ret = -EINVAL; + goto end; + } + if (vstack_ax(stack)->type != REG_S64 && vstack_bx(stack)->type != REG_DOUBLE) { + ERR("Double-S64 operator has unexpected register types\n"); + ret = -EINVAL; + goto end; + } + break; + } + + case FILTER_OP_EQ_S64_DOUBLE: + case FILTER_OP_NE_S64_DOUBLE: + case FILTER_OP_GT_S64_DOUBLE: + case FILTER_OP_LT_S64_DOUBLE: + case FILTER_OP_GE_S64_DOUBLE: + case FILTER_OP_LE_S64_DOUBLE: + { + if (!vstack_ax(stack) || !vstack_bx(stack)) { + ERR("Empty stack\n"); + ret = -EINVAL; + goto end; + } + if (vstack_ax(stack)->type != REG_DOUBLE && vstack_bx(stack)->type != REG_S64) { + ERR("S64-Double operator has unexpected register types\n"); ret = -EINVAL; goto end; } @@ -689,6 +768,7 @@ int validate_instruction_all_contexts(struct bytecode_runtime *bytecode, unsigned long target_pc = pc - start_pc; struct cds_lfht_iter iter; struct cds_lfht_node *node; + struct lfht_mp_node *mp_node; unsigned long hash; /* Validate the context resulting from the previous instruction */ @@ -699,19 +779,21 @@ int validate_instruction_all_contexts(struct bytecode_runtime *bytecode, /* Validate merge points */ hash = lttng_hash_mix((const void *) target_pc, sizeof(target_pc), lttng_hash_seed); - cds_lfht_for_each_duplicate(merge_points, hash, lttng_hash_match, - (const void *) target_pc, &iter, node) { - struct lfht_mp_node *mp_node = - caa_container_of(node, struct lfht_mp_node, node); + cds_lfht_lookup(merge_points, hash, lttng_hash_match, + (const void *) target_pc, &iter); + node = cds_lfht_iter_get_node(&iter); + if (node) { + mp_node = caa_container_of(node, struct lfht_mp_node, node); dbg_printf("Filter: validate merge point at offset %lu\n", target_pc); - ret = validate_instruction_context(bytecode, &mp_node->stack, - start_pc, pc); - if (ret) - return ret; + if (merge_points_compare(stack, &mp_node->stack)) { + ERR("Merge points differ for offset %lu\n", + target_pc); + return -EINVAL; + } /* Once validated, we can remove the merge point */ - dbg_printf("Filter: remove one merge point at offset %lu\n", + dbg_printf("Filter: remove merge point at offset %lu\n", target_pc); ret = cds_lfht_del(merge_points, node); assert(!ret); @@ -747,6 +829,11 @@ int exec_insn(struct bytecode_runtime *bytecode, case FILTER_OP_RETURN: { + if (!vstack_ax(stack)) { + ERR("Empty stack\n"); + ret = -EINVAL; + goto end; + } ret = 0; goto end; } @@ -793,6 +880,18 @@ int exec_insn(struct bytecode_runtime *bytecode, case FILTER_OP_LT_DOUBLE: case FILTER_OP_GE_DOUBLE: case FILTER_OP_LE_DOUBLE: + case FILTER_OP_EQ_DOUBLE_S64: + case FILTER_OP_NE_DOUBLE_S64: + case FILTER_OP_GT_DOUBLE_S64: + case FILTER_OP_LT_DOUBLE_S64: + case FILTER_OP_GE_DOUBLE_S64: + case FILTER_OP_LE_DOUBLE_S64: + case FILTER_OP_EQ_S64_DOUBLE: + case FILTER_OP_NE_S64_DOUBLE: + case FILTER_OP_GT_S64_DOUBLE: + case FILTER_OP_LT_S64_DOUBLE: + case FILTER_OP_GE_S64_DOUBLE: + case FILTER_OP_LE_S64_DOUBLE: { /* Pop 2, push 1 */ if (vstack_pop(stack)) { @@ -851,13 +950,18 @@ int exec_insn(struct bytecode_runtime *bytecode, int merge_ret; /* Add merge point to table */ - merge_ret = merge_point_add(merge_points, insn->skip_offset, - stack); + merge_ret = merge_point_add_check(merge_points, + insn->skip_offset, stack); if (merge_ret) { ret = merge_ret; goto end; } /* Continue to next instruction */ + /* Pop 1 when jump not taken */ + if (vstack_pop(stack)) { + ret = -EINVAL; + goto end; + } next_pc += sizeof(struct logical_op); break; } @@ -995,9 +1099,10 @@ int lttng_filter_validate_bytecode(struct bytecode_runtime *bytecode) 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; + ret = bytecode_validate_overflow(bytecode, start_pc, pc); + if (ret != 0) { + if (ret == -ERANGE) + ERR("filter bytecode overflow\n"); goto end; } dbg_printf("Validating op %s (%u)\n",