Example #1
0
def test_parse_complex_cfg():

    src_to_test = """
                    int main()
                    {
                        if(true){
                            test = 1;
                        }
                        var = 0;
                        if(true){
                            if(true){
                                test = 2;
                            }else{
                                test = 3;
                            }
                        }else{
                            test = 3;
                        }
                    }
                    """

    src_to_test_ast = parser.parse(src_to_test)
    main_ast = cast_lib.find_funcdef_node(src_to_test_ast, 'main')

    codecfg = cfg.ControlFlowGraph(main_ast)

    #codecfg.draw()

    assert len(codecfg.nodes()) == 16
    assert len(codecfg.edges()) == 18
    assert nx.is_directed_acyclic_graph(codecfg)

    assert_degrees(codecfg, 3)
Example #2
0
def test_parse_while_no_loop():

    src_to_test = """
                    int main(){
                        while(true){
                            if(true){
                                test = 2;
                            }
                            var = 0;
                        }
                    }
                    """

    src_to_test_ast = parser.parse(src_to_test)
    main_ast = cast_lib.find_funcdef_node(src_to_test_ast, 'main')

    codecfg = cfg.ControlFlowGraph(main_ast, loop=False)

    #codecfg.draw()

    assert len(codecfg.nodes()) == 9
    assert len(codecfg.edges()) == 10
    assert nx.is_directed_acyclic_graph(codecfg)

    assert_degrees(codecfg, 2)
Example #3
0
def path_to_formula(src, theory):

    src1_path_ast = parser.parse(cast_lib.remove_c99_comments(src))
    main_ast = cast_lib.find_funcdef_node(src1_path_ast,'main')
    srccfg = cfg.ControlFlowGraph(main_ast)

    path = list(srccfg.paths())[0]
    phi = PhaseFormula(theory, path)

    return phi
Example #4
0
def complete_paths(suffix_path, prefix_paths):
    """ Generate all valid paths extending `prefix_paths` with `suffix path`"""

    complete_paths = []

    for prefix_path in prefix_paths:
        # we don't prefix with ended paths, i.e. with continues
        count_continues = cast_lib.count_continues(prefix_path)

        if count_continues == 0:
            cfg_end = suffix_path.copy()
            # CFG path only containing ifs
            prefix_nodes = [
                n for n in list(nx.topological_sort(prefix_path))
                if type(n) == c_ast.If or type(n) == c_ast.While
            ]
            #prefix_nodes = list(nx.topological_sort(prefix_path))
            # discard the node in common, the last
            #prefix_nodes = prefix_nodes[:-1]
            #prefix_nodes.append(c_ast.Break())

            valid_prefix_path = cfg.ControlFlowGraph()
            nx.add_path(valid_prefix_path, prefix_nodes)

            # TODO: we could iterate the path and check if is SAT or
            # reduce redundant ifs

            if len(valid_prefix_path) > 0:
                nodes_beginning = list(nx.topological_sort(valid_prefix_path))
                nodes_end = list(nx.topological_sort(cfg_end))

                complete_path = nx.compose(valid_prefix_path, cfg_end)
                complete_path.add_edge(nodes_beginning[-1], nodes_end[0])

                path = complete_path
            else:
                path = cfg_end

            complete_paths.append(path)

    return complete_paths
Example #5
0
def test_parse_simple_cfg():

    src_to_test = """
                    int main(){
                        while(true){
                            if(true){
                                test = 2;
                            }
                            var = 0;
                        }
                    }
                    """

    src_to_test_ast = parser.parse(src_to_test)
    main_ast = cast_lib.find_funcdef_node(src_to_test_ast, 'main')

    codecfg = cfg.ControlFlowGraph(main_ast)

    #codecfg.draw()

    assert len(codecfg.nodes()) == 9
    assert len(codecfg.edges()) == 10

    assert_degrees(codecfg, 1)
Example #6
0
def prune_equivalent_paths(ast: c_ast.Node):

    theory = C99Theory(ast)

    code_ast = cast_lib.find_funcdef_node(ast, 'main')
    code_cfg = cfg.ControlFlowGraph(code_ast, loop=False)

    # collect the paths
    to_visit = phase_paths(code_cfg)

    # we split paths between mbox assignments and phase changes
    splitted_paths = []

    for p in to_visit:

        splitted_path = []
        chunk = []

        for n_index, node in enumerate(p):

            chunk.append(node)

            phase_incremented = cast_lib.is_var_increment(node.astnode, 'view')
            phase_jump = cast_lib.is_var_assignment(node.astnode, 'view')
            path_end = type(node.astnode) == c_ast.Continue

            if (phase_incremented or phase_jump
                    or path_end) and len(chunk) > 0:
                # add context from previous chunk
                if len(splitted_path) > 0:
                    previous_chunk = splitted_path[-1]

                    context_chunk = []
                    # we look for all asserts from the last elemento to the first mbox assignment
                    for n_prev in previous_chunk[::-1]:
                        if cast_lib.is_var_assignment(n_prev.astnode, 'mbox'):
                            break

                        if type(n_prev) == cfg.ControlFlowGraph.AssertNode:
                            context_chunk.insert(0, n_prev)

                    context_chunk.extend(chunk)
                    chunk = context_chunk

                splitted_path.append(chunk)
                chunk = []

        splitted_paths.append(splitted_path)

    # search for equivalent paths respecting the phase
    final_paths = [path[1] for path in splitted_paths]

    # BFS search of chunk equivalence
    max_splitted_path = max(splitted_paths, key=lambda i: len(i))
    max_splitted_path_length = len(max_splitted_path)

    for depth in range(0, max_splitted_path_length):
        for path_split in splitted_paths:
            if len(path_split) > depth:
                chunk = path_split[depth]
                # look for an equivalent phase
                phi = PhaseFormula(theory, chunk)
                unique = True

                for p in final_paths:
                    if p != chunk:
                        phi_prime = PhaseFormula(theory, p)
                        if phi == phi_prime:
                            unique = False
                            break

                if unique:
                    final_paths.append(chunk)
Example #7
0
def async_to_sync(async_ast: c_ast.Node, config):
    """ Given a c99 code in a AST form, returns the corresponding code of its
    synchronous equivalent program.

    Notes
    -----
    First step is calculate all code between round variables assigments, then
    we add all the context needed to reach that piece of code.

    Entry point     if  ... if ...      Current round            Next round
    * --------------------------------> *----------------------> *
                    A                               B
    
    The code we want to extract is in path B, but we need to collect all the
    conditions to reach this path, this is obtained collection all c_ast.If
    in path A.

    Path A and B can't contain c_ast.Continue nodes. Path B can't contain other
    round assigments in the middle.
    """
    phase_variable = config['phase']
    round_variable = config['round']
    labels = config['labels']

    # we discard what we won't use
    main_ast = cast_lib.find_funcdef_node(async_ast, 'main')
    cast_lib.map_dfs(main_ast, cast_lib.replace_while_with_body, [])
    cast_lib.filter_nodes(main_ast, remove_declarations)

    codecfg = cfg.ControlFlowGraph(main_ast)

    # we search paths between every (monotonically increasing) assigment of round variables
    paths_between_rounds = paths_between_round_assignments(
        codecfg, labels, round_variable, phase_variable)

    # for every protocol's round we calculate all possible paths including its previous context (e.g., ifs conditions)
    start_node = list(nx.topological_sort(codecfg))[0]
    complete_paths_by_round = {}

    for round_label, suffix_paths in paths_between_rounds.items():
        complete_paths_by_round[round_label] = []

        for suffix_path in suffix_paths:
            suffix_first_node = list(nx.topological_sort(suffix_path))[0]
            prefix_paths = get_cfg_paths_between_nodes(codecfg, start_node,
                                                       suffix_first_node)

            cp = complete_paths(suffix_path, prefix_paths)
            complete_paths_by_round[round_label].extend(cp)

    # the code of a round is the (graph) union of all the complete paths found to belong to that round
    # the easiest approach is to remove the nodes not included in those paths from the original code using the coord property
    sync_code = {}

    for round_label, paths in complete_paths_by_round.items():

        round_code_cfg = cfg.ControlFlowGraph()
        nodes_to_keep = set()

        for p in paths:
            for n in p.nodes():
                nodes_to_keep.add(str(n.coord))

        round_sync_code = copy.deepcopy(main_ast)
        cast_lib.filter_nodes(round_sync_code, node_coord_not_in_set,
                              nodes_to_keep)
        sync_code[round_label] = round_sync_code

    # translate to CompHO
    compho = {}

    for round_label, ast_code in sync_code.items():
        compho[round_label] = {}
        ast_send = c_ast.FileAST([copy.deepcopy(ast_code)])
        get_compho_send(ast_send)
        ast_update = c_ast.FileAST([copy.deepcopy(ast_code)])
        get_compho_update(ast_update, round_variable, round_label)
        compho[round_label]['send'] = ast_send
        compho[round_label]['update'] = ast_update

    return compho