Add M4 macros helpers to build Java programs
[lttng-ust.git] / liblttng-ust / lttng-filter-validator.c
index 239bda2f76ea931820d9f6a1361b4d65baf2ffe4..6cdfd8c164764e075bab11a5cb8234c51765890f 100644 (file)
@@ -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,30 +281,41 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode,
        {
                if (unlikely(pc + sizeof(struct logical_op)
                                > start_pc + bytecode->len)) {
-                       ret = -EINVAL;
+                       ret = -ERANGE;
                }
                break;
        }
 
-       /* load */
+       /* load field ref */
        case FILTER_OP_LOAD_FIELD_REF:
        {
                ERR("Unknown field ref type\n");
                ret = -EINVAL;
                break;
        }
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_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:
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
+       case FILTER_OP_GET_CONTEXT_REF_S64:
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
        {
                if (unlikely(pc + sizeof(struct load_op) + sizeof(struct field_ref)
                                > start_pc + bytecode->len)) {
-                       ret = -EINVAL;
+                       ret = -ERANGE;
                }
                break;
        }
 
+       /* load from immediate operand */
        case FILTER_OP_LOAD_STRING:
        {
                struct load_op *insn = (struct load_op *) pc;
@@ -267,7 +323,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 +331,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 +340,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 +349,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,10 +360,11 @@ int bytecode_validate_overflow(struct bytecode_runtime *bytecode,
        {
                if (unlikely(pc + sizeof(struct cast_op)
                                > start_pc + bytecode->len)) {
-                       ret = -EINVAL;
+                       ret = -ERANGE;
                }
                break;
        }
+
        }
 
        return ret;
@@ -475,14 +532,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;
                }
@@ -578,7 +669,7 @@ int validate_instruction_context(struct bytecode_runtime *bytecode,
                break;
        }
 
-       /* load */
+       /* load field ref */
        case FILTER_OP_LOAD_FIELD_REF:
        {
                ERR("Unknown field ref type\n");
@@ -614,6 +705,7 @@ int validate_instruction_context(struct bytecode_runtime *bytecode,
                break;
        }
 
+       /* load from immediate operand */
        case FILTER_OP_LOAD_STRING:
        {
                break;
@@ -668,6 +760,41 @@ int validate_instruction_context(struct bytecode_runtime *bytecode,
                break;
        }
 
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_REF:
+       {
+               ERR("Unknown get context ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printf("Validate get context ref offset %u type string\n",
+                       ref->offset);
+               break;
+       }
+       case FILTER_OP_GET_CONTEXT_REF_S64:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printf("Validate get context ref offset %u type s64\n",
+                       ref->offset);
+               break;
+       }
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
+       {
+               struct load_op *insn = (struct load_op *) pc;
+               struct field_ref *ref = (struct field_ref *) insn->data;
+
+               dbg_printf("Validate get context ref offset %u type double\n",
+                       ref->offset);
+               break;
+       }
+
        }
 end:
        return ret;
@@ -689,6 +816,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 +827,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 +877,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 +928,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,26 +998,39 @@ 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;
        }
 
-       /* load */
+       /* load field ref */
        case FILTER_OP_LOAD_FIELD_REF:
        {
                ERR("Unknown field ref type\n");
                ret = -EINVAL;
                goto end;
        }
+       /* get context ref */
+       case FILTER_OP_GET_CONTEXT_REF:
+       {
+               ERR("Unknown get context ref type\n");
+               ret = -EINVAL;
+               goto end;
+       }
        case FILTER_OP_LOAD_FIELD_REF_STRING:
        case FILTER_OP_LOAD_FIELD_REF_SEQUENCE:
+       case FILTER_OP_GET_CONTEXT_REF_STRING:
        {
                if (vstack_push(stack)) {
                        ret = -EINVAL;
@@ -881,6 +1041,7 @@ int exec_insn(struct bytecode_runtime *bytecode,
                break;
        }
        case FILTER_OP_LOAD_FIELD_REF_S64:
+       case FILTER_OP_GET_CONTEXT_REF_S64:
        {
                if (vstack_push(stack)) {
                        ret = -EINVAL;
@@ -891,6 +1052,7 @@ int exec_insn(struct bytecode_runtime *bytecode,
                break;
        }
        case FILTER_OP_LOAD_FIELD_REF_DOUBLE:
+       case FILTER_OP_GET_CONTEXT_REF_DOUBLE:
        {
                if (vstack_push(stack)) {
                        ret = -EINVAL;
@@ -901,6 +1063,7 @@ int exec_insn(struct bytecode_runtime *bytecode,
                break;
        }
 
+       /* load from immediate operand */
        case FILTER_OP_LOAD_STRING:
        {
                struct load_op *insn = (struct load_op *) pc;
@@ -995,9 +1158,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",
This page took 0.028096 seconds and 4 git commands to generate.