Exemple #1
0
 def get_type(stordefs):
     sizes = set()
     offsets = set()
     for s in stordefs:
         if (m := match(
                 s, ("stor", ":size", ":off",
                     (":op", ":idx", ...)))) and m.op in ("map", "array"):
             sizes.add(m.size)
             if safe_le_op(0, m.off) is True:
                 offsets.add(m.off)
Exemple #2
0
def slice_exp(exp, left, right):
    size = sub_op(right, left)

    logger.debug(f"slicing {exp}, offset {left} bytes, until {right} bytes")
    # e.g. mem[32 len 10], 2, 4 == mem[34,2]

    if m := match(exp, ("mem", ("range", ":rleft", ":rlen"))):
        rleft, rlen = m.rleft, m.rlen
        if safe_le_op(add_op(left, size), rlen):
            return ("mem", ("range", add_op(rleft, left), size))
        else:
            return None
Exemple #3
0
def memloc_overwrite(memloc, split):
    # returns mem ranges excluding the ones that are *for sure* overwritten by 'split'
    # e.g. overwrites(('range', 64, 32), ('range', 70, 10)) -> [('range', 64, 6), (range, 80, 16)]
    # e.g. overwrites(('range', 64, 32), ('range', 70, 'unknown')) -> [('range', 64, 32)], bc. 'unknown' can be 0

    op, m_left, m_len = memloc
    assert op == "range"
    op, s_left, s_len = split
    assert op == "range"

    m_right = add_op(m_left, m_len)
    s_right = add_op(s_left, s_len)

    if safe_le_op(m_right, s_left) is True:  # split after memory - no overlap
        return [memloc]
    if safe_le_op(s_right, m_left) is True:  # split before memory - no overlap
        return [memloc]

    left_len = sub_op(s_left, m_left)
    right_len = sub_op(m_right, s_right)

    range_left = ("range", m_left, left_len)
    range_right = ("range", s_right, right_len)

    left_ge_zero, right_ge_zero = safe_ge_zero(left_len), safe_ge_zero(
        right_len)

    if left_ge_zero is None or right_ge_zero is None:
        # we can't compare some numbers, conservatively return whole range
        return [memloc]

    res = []

    if safe_ge_zero(left_len) is True and left_len != 0:
        res.append(range_left)

    if safe_ge_zero(right_len) is True and right_len != 0:
        res.append(range_right)

    return res
Exemple #4
0
def apply_mask_to_range(memloc, size, offset):
    op, range_pos, range_len = memloc
    assert op == "range"

    size_bytes, size_bits = to_bytes(size)
    offset_bytes, offset_bits = to_bytes(offset)

    assert offset_bits == size_bits == 0, (offset_bits, size_bits)  # for now
    assert safe_le_op(add_op(size_bytes, offset_bytes), range_len) is True, (
        size_bytes,
        offset_bytes,
        range_len,
    )  # otherwise we need to learn to handle that

    range_pos = add_op(range_pos,
                       sub_op(range_len, add_op(size_bytes, offset_bytes)))
    range_len = size_bytes  # sub_op(range_len, add_op(offset_bytes, size_bytes))

    return ("range", range_pos, range_len)
Exemple #5
0
def _fill_mem(exp, split, split_val):
    if exp == ("mem", split):
        return split_val

    op, memloc = exp
    assert op == "mem"
    op, m_left, m_len = memloc
    assert op == "range"
    op, s_left, s_len = split
    assert op == "range"

    m_right = add_op(m_left, m_len)
    s_right = add_op(s_left, s_len)

    logger.debug(f"orig memloc: {m_left} len {m_len} right {m_right}")
    logger.debug(f"split memloc: {s_left} len {s_len} right {s_right}")

    if (
            safe_le_op(m_right, s_left) is not False
    ):  # if the split is before memory, or we can't compare - not replacing
        logger.debug("split before memory or can't compare - not replacing")
        return exp

    if safe_le_op(s_right, m_left) is not False:  # -,,- after memory
        logger.debug("split after memory or can't compare - not replacing")
        return exp

    left = safe_max_op(s_left, m_left)
    right = safe_min_op(s_right, m_right)

    logger.debug(f"split begins at {left} ends at {right}")

    if left is None or right is None:
        return exp  # if we can't figure out which one is smaller/larger, we're not replacing

    memloc, memloc_max = replace_max_with_MAX(memloc)
    split, split_max = replace_max_with_MAX(split)
    # 'max' op tends to mess up with all the algebra stuff, so we're replacing
    # it with a variable 'MAX' for the time being

    if split_max != memloc_max:
        logger.warning("different maxes")
        return exp

    # by now we know:
    # - the split overlaps memory for sure
    # - we know the boundaries of split
    # - so we now return data (before_split, split_val, after_split)

    res_left = slice_exp(exp, 0, sub_op(left, m_left))
    if res_left is None:
        return exp
    logger.debug(f"value left untouched on left: {res_left}")

    res_right = slice_exp(exp, sub_op(right, m_left), sub_op(m_right, m_left))
    if res_right is None:
        return exp

    logger.debug(f"value right untouched on right: {res_right}")

    res = []

    if safe_gt_zero(sizeof(res_left)) is True:
        logger.debug("size of left untouched > 0, adding to output")
        res.append(res_left)

    elif safe_gt_zero(sizeof(res_left)) is None:
        logger.debug("we don't know if left size > 0, aborting")
        return exp

    center_in_start = sub_op(left, s_left)
    center_in_len = sub_op(right, s_left)

    logger.debug(
        f"inserted value offset {center_in_start}, length {center_in_len}")
    logger.debug(f"cutting this out of {split_val}")

    res_center = slice_exp(split_val, center_in_start, center_in_len)

    logger.debug(f"inserted value after slicing: {res_center}")

    if res_center is None:
        return exp

    if safe_ge_zero(sizeof(res_center)) is True:
        res.append(res_center)
    else:
        assert False, sizeof(
            res_center)  # this shouldn't happen considering the above checks?

    if safe_ge_zero(sizeof(res_right)) is True:
        if sizeof(res_right) != 0:
            res.append(res_right)
    elif safe_ge_zero(sizeof(res_right)) is None:
        return exp

    assert None not in res

    return ("data", ) + tuple(res)
Exemple #6
0
def splits_mem(memloc, split, memval, split_val=None):
    # returns memory values we can be confident of, after overwriting the split part of memory

    op, m_left, m_len = memloc
    assert op == "range"
    op, s_left, s_len = split
    assert op == "range"

    m_right = add_op(m_left, m_len)
    s_right = add_op(s_left, s_len)

    logger.debug(f"applying split [{s_left} (len {s_len}) {s_right}]")
    logger.debug(f"            to [{m_left} (len {m_len}) {m_right}]")

    if not safe_ge_zero(s_len):
        s_len = "undefined"
        s_right = add_op(s_left, s_len)

    if safe_le_op(m_right, s_left) is True:  # split after memory - no overlap
        return [(memloc, memval)]

    if safe_le_op(s_right, m_left) is True:  # split before memory - no overlap
        return [(memloc, memval)]

    left = safe_max_op(s_left, m_left)
    right = safe_min_op(s_right, m_right)

    logger.debug(f"split overwrites memory from {left} to {right}")

    # left/right relative to beginning of memory location
    in_left = sub_op(left, m_left)
    in_right = sub_op(right, m_left)

    logger.debug(f"that is, relative to memloc {in_left} to {in_right}")
    if safe_le_op(in_left, m_len) is not True or left is None:
        logger.debug(
            f"we are not sure that m_len: {m_len} is bigger than beginning of split, returning []"
        )
        return []

    assert in_left == 0 if safe_le_op(right, m_left) else True

    val_left = slice_exp(memval, 0, in_left) if left is not None else None
    val_right = (slice_exp(memval, in_right, sub_op(m_right, m_left))
                 if right is not None else None)
    res = []

    left_len = sub_op(left, m_left)  # sizeof(val_left)
    right_len = sub_op(m_right, right)

    if safe_ge_zero(
            left_len) is True and left_len != 0 and val_left is not None:
        res.append((("range", m_left, left_len), val_left))

    if split_val is not None:
        center_left = safe_max_op(m_left, s_left)
        center_right = safe_min_op(m_right, s_right)

        center_len = sub_op(center_right, center_left)

        if is_array(opcode(split_val)):  # in ARRAY_OPCODES:
            # mem[a len b] = calldata[x len b]
            # log mem[c len d]
            # -> calldata[x+ c - a, center_len]
            arr_offset, arr_len = split_val[1:]
            center_offset = add_op(arr_offset, sub_op(center_left, s_left))
            center_val = (opcode(split_val), center_offset, center_len)

        else:
            center_offset = sub_op(s_right, center_right)
            center_val = mask_op(
                split_val,
                size=mul_op(center_len, 8),
                offset=mul_op(center_offset, 8),
                shr=mul_op(center_offset, 8),
            )

        center_range = ("range", center_left, center_len)

        if safe_ge_zero(center_len) and center_len != 0:
            res.append((center_range, center_val))

    if safe_ge_zero(
            right_len) is True and right_len != 0 and val_right is not None:
        res.append((("range", right, right_len), val_right))

    return res
Exemple #7
0
            lines.append(("store", off, 0, idx, 0))
        #        lines.append(('store', size, off, idx, ('storage', size, off, idx)))
        if size + off < 256:
            lines.append(("store", (256 - size - off), size + off, idx, 0))

        return lines

    if m := match(line, ("store", 256, 0, ":idx", ":val")):
        size = 256
        off = 0
        idx, val = m.idx, m.val
        splitted = split_or(val)

        res = []
        for s_size, s_off, s_val in splitted:
            if safe_le_op(off, s_off) and safe_le_op(add_op(s_size, s_off),
                                                     add_op(off, size)):
                if s_val != (
                        "storage",
                        s_size,
                        s_off,
                        idx,
                ):  # ignore writing the same to the same storage
                    res.append(("store", s_size, s_off, idx, s_val))
            else:
                logger.warning("unusual store")
                return [line]

        return res
    else:
        return [line]
Exemple #8
0
            m.name):  # in ('call.data', 'ext_call.return_data'):
        if m.size == 32:
            return m.name + f"[{pret(m.offset)}]"
        else:
            return m.name + f"[{pret(m.offset)} len {pret(m.size)}]"

    if ((m := match(
            exp,
        (
            "mask_shl",
            ":size",
            ":offset",
            ":shl",
            ("stor", ":s_size", ":s_off", ":s_idx"),
        ),
    )) and safe_le_op(m.s_size, m.size) and m.shl == 0):
        return pret(("stor", m.s_size, m.s_off, m.s_idx))

    if opcode(exp) == "stor":
        return pretty_stor(exp, add_color=add_color)

    if opcode(exp) == "type":
        return pretty_stor(exp, add_color=add_color)

    if opcode(exp) == "field":
        return pretty_stor(exp, add_color=add_color)

    if m := match(exp, ("cd", ":num")):
        if m.num == 0:
            return col("call.func_hash", C.green)
        parsed_exp = get_param_name(exp, add_color=add_color)