Filter: validate range overflow with end of insn
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 16 Jul 2012 15:15:02 +0000 (11:15 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 16 Jul 2012 15:15:02 +0000 (11:15 -0400)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
liblttng-ust/lttng-filter.c

index 19775c3098c939a3d1d9b496e453c6cfd49dcd3d..f9d512c955d571d63f5b8b6daa0b9488f214baac 100644 (file)
@@ -881,6 +881,186 @@ error_mismatch:
        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)
 {
@@ -897,7 +1077,7 @@ 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 (unlikely(pc >= start_pc + bytecode->len)) {
+               if (bytecode_validate_overflow(bytecode, start_pc, pc) != 0) {
                        ERR("filter bytecode overflow\n");
                        ret = -EINVAL;
                        goto end;
This page took 0.034983 seconds and 4 git commands to generate.