Re: [PATCH] libsepol/cil: improve recursion detection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 09/15/2015 11:02 AM, James Carter wrote:
> On 09/15/2015 08:56 AM, Steve Lawrence wrote:
>> Add support for detecting recursive blockinherits, and print a trace of
>> the detected loop. Output will look something like this upon detection:
>>
>>    Recursive blockinherit found:
>>      test.cil:42: block a
>>      test.cil:43: blockinherit b
>>      test.cil:36: block b
>>      test.cil:37: blockinherit c
>>      test.cil:39: block c
>>      test.cil:40: blockinherit a
>>
>> Additionally, improve support for detecting recursive macros/calls. Due
>> to the way calls are copied, the existing code only detected recursion
>> with call depth of three or more. Smaller depths, like
>>
>>    (macro m ()
>>      (call m))
>>
>> were not detected and caused a segfault. The callstack that was used for
>> this was not sufficient, so that is removed and replaced with a method
>> similar to the block recursion detection. A similar trace is also
>> displayed for recursive macros/calls.
>>
>> Also, cleanup sidorder, classorder, catorder, sensorder, and in lists at
>> the end of resolve, fixing a potential memory leak if errors occur
>> during resolve.
>>
>> Signed-off-by: Steve Lawrence <slawrence@xxxxxxxxxx>
>> ---
>>   libsepol/cil/src/cil_resolve_ast.c | 193
>> +++++++++++++++++++++++++++++--------
>>   1 file changed, 155 insertions(+), 38 deletions(-)
>>
>> diff --git a/libsepol/cil/src/cil_resolve_ast.c
>> b/libsepol/cil/src/cil_resolve_ast.c
>> index 0df5c63..79cece5 100644
>> --- a/libsepol/cil/src/cil_resolve_ast.c
>> +++ b/libsepol/cil/src/cil_resolve_ast.c
>> @@ -52,7 +52,6 @@ struct cil_args_resolve {
>>       enum cil_pass pass;
>>       uint32_t *changed;
>>       char *last_resolved_name;
>> -    struct cil_tree_node *callstack;
>>       struct cil_tree_node *optstack;
>>       struct cil_tree_node *boolif;
>>       struct cil_tree_node *macro;
>> @@ -2210,6 +2209,70 @@ exit:
>>       return rc;
>>   }
>>
>> +int cil_check_recursive_blockinherit(struct cil_tree_node *bi_node)
>> +{
>> +    struct cil_tree_node *curr = NULL;
>> +    struct cil_blockinherit *bi = NULL;
>> +    struct cil_block *block = NULL;
>> +    struct cil_list_item *item = NULL;
>> +    struct cil_list *trace = NULL;
>> +    int rc = SEPOL_ERR;
>> +
>> +    bi = bi_node->data;
>> +
>> +    for (curr = bi_node->parent; curr != NULL; curr = curr->parent) {
>> +        if (curr->flavor != CIL_BLOCK) {
>> +            continue;
>> +        }
>> +
>> +        block = curr->data;
>> +
>> +        if (block != bi->block) {
>> +            continue;
>> +        }
>> +
>> +        cil_log(CIL_ERR, "Recursive blockinherit found:\n");
>> +
>> +        cil_list_init(&trace, CIL_NODE);
>> +
>> +        for (curr = bi_node; curr != NULL; curr = curr->parent) {
> 
> Is the reuse of curr for this for loop intentional?
> Could this cause us to not check everything in the above for loop?
> Would curr ever be NULL in this loop? It seems like the loop will always
> end when curr == NODE(block), so why not just make that the terminating
> condition.
> 
> 

If we ever get to this point, it means a loop was definitely found, and
we'll never continue on the outer loop. So the original curr isn't
really needed anymore. I could use a different name to clarify. Another
option might be to set a flag, break out of the loop, and then enter
another loop that builds the list and prints. This way we wouldn't have
these nested loops.

Yes, I think we can break when curr == NODE(block)

>> +            if (curr->flavor == CIL_BLOCK) {
>> +                cil_list_prepend(trace, CIL_NODE, curr);
>> +            } else {
>> +                if (curr != bi_node) {
>> +                    cil_list_prepend(trace, CIL_NODE, NODE(((struct
>> cil_blockinherit *)curr->data)->block));
>> +                }
>> +                cil_list_prepend(trace, CIL_NODE, curr);
>> +            }
>> +
>> +            if (curr == NODE(block)) {
>> +                break;
>> +            }
>> +        }
>> +
>> +        cil_list_for_each(item, trace) {
>> +            curr = item->data;
>> +            cil_log(CIL_ERR, "  %s:%d: ", curr->path, curr->line);
>> +
>> +            if (curr->flavor == CIL_BLOCK) {
>> +                cil_log(CIL_ERR, "block %s\n", DATUM(curr->data)->name);
>> +            } else {
>> +                cil_log(CIL_ERR, "blockinherit %s\n", ((struct
>> cil_blockinherit *)curr->data)->block_str);
>> +            }
>> +        }
>> +
>> +        cil_list_destroy(&trace, CIL_FALSE);
>> +
>> +        rc = SEPOL_ERR;
>> +        goto exit;
>> +    }
>> +
>> +    rc = SEPOL_OK;
>> +
>> +exit:
>> +    return rc;
>> +}
>> +
>>   int cil_resolve_blockinherit_copy(struct cil_tree_node *current,
>> void *extra_args)
>>   {
>>       struct cil_block *block = current->data;
>> @@ -2233,6 +2296,11 @@ int cil_resolve_blockinherit_copy(struct
>> cil_tree_node *current, void *extra_arg
>>       }
>>
>>       cil_list_for_each(item, block->bi_nodes) {
>> +        rc = cil_check_recursive_blockinherit(item->data);
>> +        if (rc != SEPOL_OK) {
>> +            goto exit;
>> +        }
>> +
>>           rc = cil_copy_ast(db, current, item->data);
>>           if (rc != SEPOL_OK) {
>>               cil_log(CIL_ERR, "Failed to copy block contents into
>> blockinherit\n");
>> @@ -2500,6 +2568,77 @@ exit:
>>       return rc;
>>   }
>>
>> +int cil_check_recursive_call(struct cil_tree_node *call_node, struct
>> cil_tree_node *macro_node)
>> +{
>> +    struct cil_tree_node *curr = NULL;
>> +    struct cil_list *trace = NULL;
>> +    struct cil_list_item *item = NULL;
>> +    struct cil_call * call = NULL;
>> +    struct cil_tree_node *terminating_node = NULL;
>> +    int rc = SEPOL_ERR;
>> +
>> +    for (curr = call_node; curr != NULL; curr = curr->parent) {
>> +        if (curr->flavor == CIL_CALL) {
>> +            if (curr == call_node) {
>> +                continue;
>> +            }
>> +
>> +            call = curr->data;
>> +            if (call->macro != macro_node->data) {
>> +                continue;
>> +            }
>> +        } else if (curr->flavor == CIL_MACRO) {
>> +            if (curr != macro_node) {
>> +                rc = SEPOL_OK;
>> +                goto exit;
>> +            }
>> +        } else {
>> +            continue;
>> +        }
>> +
>> +        cil_log(CIL_ERR, "Recursive macro call found:\n");
>> +        cil_list_init(&trace, CIL_NODE);
>> +
>> +        terminating_node = curr;
>> +
>> +        for (curr = call_node; curr != terminating_node; curr =
>> curr->parent) {
> 
> I see that curr will end up back at the same value as it started, so
> this will work, but it is not the clearest code.
> 

Do you think it would be cleaner to break out of this loop with a flag
indicating that recursion was found, and then do the printing after the
loop? Or maybe move the trace list generation and printing into another
function? I think there's only one or two parameters necessary to print
the trace, so it shouldn't be too difficult/messy.

> 
>> +            if (curr->flavor == CIL_CALL) {
>> +                if (curr != call_node) {
>> +                    cil_list_prepend(trace, CIL_NODE, NODE(((struct
>> cil_call *)curr->data)->macro));
>> +                }
>> +                cil_list_prepend(trace, CIL_NODE, curr);
>> +            }
>> +        }
>> +
>> +        if (terminating_node->flavor == CIL_MACRO) {
>> +            cil_list_prepend(trace, CIL_NODE, terminating_node);
>> +        } else {
>> +            call = terminating_node->data;
>> +            cil_list_prepend(trace, CIL_NODE, NODE(call->macro));
>> +        }
>> +
>> +        cil_list_for_each(item, trace) {
>> +            curr = item->data;
>> +            cil_log(CIL_ERR, "  %s:%d: ", curr->path, curr->line);
>> +
>> +            if (curr->flavor == CIL_MACRO) {
>> +                cil_log(CIL_ERR, "macro %s\n", DATUM(curr->data)->name);
>> +            } else {
>> +                cil_log(CIL_ERR, "call %s\n", ((struct cil_call
>> *)curr->data)->macro_str);
>> +            }
>> +        }
>> +
>> +        cil_list_destroy(&trace, CIL_FALSE);
>> +
>> +        rc = SEPOL_ERR;
>> +        goto exit;
>> +    }
>> +
>> +    rc = SEPOL_OK;
>> +exit:
>> +    return rc;
>> +}
>> +
>>   int cil_resolve_call1(struct cil_tree_node *current, void *extra_args)
>>   {
>>       struct cil_call *new_call = current->data;
>> @@ -2736,6 +2875,12 @@ int cil_resolve_call1(struct cil_tree_node
>> *current, void *extra_args)
>>
>>       if (new_call->copied == 0) {
>>           new_call->copied = 1;
>> +
>> +        rc = cil_check_recursive_call(current, macro_node);
>> +        if (rc != SEPOL_OK) {
>> +            goto exit;
>> +        }
>> +
>>           rc = cil_copy_ast(db, macro_node, current);
>>           if (rc != SEPOL_OK) {
>>               cil_log(CIL_ERR, "Failed to copy macro, rc: %d\n", rc);
>> @@ -3509,7 +3654,6 @@ int __cil_resolve_ast_first_child_helper(struct
>> cil_tree_node *current, void *ex
>>   {
>>       int rc = SEPOL_ERR;
>>       struct cil_args_resolve *args = extra_args;
>> -    struct cil_tree_node *callstack = NULL;
>>       struct cil_tree_node *optstack = NULL;
>>       struct cil_tree_node *parent = NULL;
>>       struct cil_tree_node *blockstack = NULL;
>> @@ -3519,36 +3663,18 @@ int
>> __cil_resolve_ast_first_child_helper(struct cil_tree_node *current,
>> void *ex
>>           goto exit;
>>       }
>>
>> -    callstack = args->callstack;
>>       optstack = args->optstack;
>>       parent = current->parent;
>>       blockstack = args->blockstack;
>>
>> -    if (parent->flavor == CIL_CALL || parent->flavor == CIL_OPTIONAL
>> || parent->flavor == CIL_BLOCK) {
>> +    if (parent->flavor == CIL_OPTIONAL || parent->flavor == CIL_BLOCK) {
>>           /* push this node onto a stack */
>>           cil_tree_node_init(&new);
>>
>>           new->data = parent->data;
>>           new->flavor = parent->flavor;
>>
>> -        if (parent->flavor == CIL_CALL) {
>> -            if (callstack != NULL) {
>> -                struct cil_tree_node *curr = NULL;
>> -                struct cil_call *new_call = new->data;
>> -                for (curr = callstack->cl_head; curr != NULL;
>> -                    curr = curr->cl_head) {
>> -                    struct cil_call *curr_call = curr->data;
>> -                    if (curr_call->macro == new_call->macro) {
>> -                        cil_log(CIL_ERR, "Recursive macro call
>> found\n");
>> -                        rc = SEPOL_ERR;
>> -                        goto exit;
>> -                    }
>> -                }
>> -                callstack->parent = new;
>> -                new->cl_head = callstack;
>> -            }
>> -            args->callstack = new;
>> -        } else if (parent->flavor == CIL_OPTIONAL) {
>> +        if (parent->flavor == CIL_OPTIONAL) {
>>               if (optstack != NULL) {
>>                   optstack->parent = new;
>>                   new->cl_head = optstack;
>> @@ -3587,15 +3713,7 @@ int __cil_resolve_ast_last_child_helper(struct
>> cil_tree_node *current, void *ext
>>
>>       parent = current->parent;
>>
>> -    if (parent->flavor == CIL_CALL) {
>> -        /* pop off the stack */
>> -        struct cil_tree_node *callstack = args->callstack;
>> -        args->callstack = callstack->cl_head;
>> -        if (callstack->cl_head) {
>> -            callstack->cl_head->parent = NULL;
>> -        }
>> -        free(callstack);
>> -    } else if (parent->flavor == CIL_MACRO) {
>> +    if (parent->flavor == CIL_MACRO) {
>>           args->macro = NULL;
>>       } else if (parent->flavor == CIL_OPTIONAL) {
>>           struct cil_tree_node *optstack;
>> @@ -3645,7 +3763,6 @@ int cil_resolve_ast(struct cil_db *db, struct
>> cil_tree_node *current)
>>       extra_args.pass = pass;
>>       extra_args.changed = &changed;
>>       extra_args.last_resolved_name = NULL;
>> -    extra_args.callstack = NULL;
>>       extra_args.optstack = NULL;
>>       extra_args.boolif= NULL;
>>       extra_args.macro = NULL;
>> @@ -3737,12 +3854,6 @@ int cil_resolve_ast(struct cil_db *db, struct
>> cil_tree_node *current)
>>
>>           /* reset the arguments */
>>           changed = 0;
>> -        while (extra_args.callstack != NULL) {
>> -            struct cil_tree_node *curr = extra_args.callstack;
>> -            struct cil_tree_node *next = curr->cl_head;
>> -            free(curr);
>> -            extra_args.callstack = next;
>> -        }
>>           while (extra_args.optstack != NULL) {
>>               struct cil_tree_node *curr = extra_args.optstack;
>>               struct cil_tree_node *next = curr->cl_head;
>> @@ -3764,6 +3875,12 @@ int cil_resolve_ast(struct cil_db *db, struct
>> cil_tree_node *current)
>>
>>       rc = SEPOL_OK;
>>   exit:
>> +    __cil_ordered_lists_destroy(&extra_args.sidorder_lists);
>> +    __cil_ordered_lists_destroy(&extra_args.classorder_lists);
>> +    __cil_ordered_lists_destroy(&extra_args.catorder_lists);
>> +    __cil_ordered_lists_destroy(&extra_args.sensitivityorder_lists);
>> +    cil_list_destroy(&extra_args.in_list, CIL_FALSE);
>> +
>>       return rc;
>>   }
>>
>>
> 
> 

_______________________________________________
Selinux mailing list
Selinux@xxxxxxxxxxxxx
To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx.
To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.



[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux