From 2b644c93808a428d9def9c960a95686307e8629b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 21 Feb 2009 01:36:49 +0300 Subject: [PATCH] Add support for "if (a?b:c) { ..." ternary conditions One place where this is used is in __cond_lock. #define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) I also added support so that known_condition_true() returns true for "({ __acquire(x); 1; })". Signed-off-by: Dan Carpenter --- smatch.h | 2 ++ smatch_conditions.c | 49 ++++++++++++++++++++++++++++++++++++++++++ smatch_extra.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- smatch_helper.c | 3 +++ smatch_states.c | 22 +++++++++++++++++++ validation/sm_select.c | 37 ++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 validation/sm_select.c diff --git a/smatch.h b/smatch.h index 457964ee..adf15e11 100644 --- a/smatch.h +++ b/smatch.h @@ -159,6 +159,8 @@ void __merge_false_states(); void __merge_true_states(); void __negate_cond_stacks(); +void __save_false_states_for_later(); +void __use_previously_stored_false_states(); void __use_cond_true_states(); void __use_cond_false_states(); void __push_cond_stacks(); diff --git a/smatch_conditions.c b/smatch_conditions.c index 4472b27f..a8a519f3 100644 --- a/smatch_conditions.c +++ b/smatch_conditions.c @@ -152,12 +152,57 @@ static void handle_logical(struct expression *expr) __use_cond_true_states(); } +static void handle_select(struct expression *expr) +{ + /* + * if ((aaa()?bbb():ccc())) { ... + * + * This is almost the same as: + * if ((aaa() && bbb()) || (!aaa() && ccc())) { ... + * + * It's a bit complicated because we shouldn't pass aaa() + * to the clients more than once. + */ + + split_conditions(expr->conditional); + + __save_false_states_for_later(); + __use_cond_true_states(); + + if (known_condition_true(expr->cond_true)) { + __split_expr(expr->cond_true); + } else { + __push_cond_stacks(); + split_conditions(expr->cond_true); + __and_cond_states(); + __pop_pre_cond_states(); + } + + if (is_zero(expr->cond_false)) { + __pop_pre_cond_states(); + __use_cond_true_states(); + return; + } + + __use_previously_stored_false_states(); + + __save_pre_cond_states(); + __push_cond_stacks(); + split_conditions(expr->cond_false); + __or_cond_states(); + __pop_pre_cond_states(); + + __use_cond_true_states(); +} + static void split_conditions(struct expression *expr) { SM_DEBUG("%d in split_conditions type=%d\n", get_lineno(), expr->type); expr = strip_expr(expr); + if (!expr) + return; switch(expr->type) { case EXPR_LOGICAL: @@ -175,6 +220,10 @@ static void split_conditions(struct expression *expr) if (handle_preop(expr)) return; break; + case EXPR_CONDITIONAL: + case EXPR_SELECT: + handle_select(expr); + return; } __pass_to_client(expr, CONDITION_HOOK); diff --git a/smatch_extra.c b/smatch_extra.c index 6f997a2e..3602afe0 100644 --- a/smatch_extra.c +++ b/smatch_extra.c @@ -184,13 +184,10 @@ static int true_comparison(int left, int comparison, int right) return 0; } -int known_condition_true(struct expression *expr) +static int compare_true(struct expression *expr) { int left, right, ret; - if (!expr || expr->type != EXPR_COMPARE) - return 0; - if ((left = expr_to_val(expr->left)) == UNDEFINED) return 0; @@ -206,4 +203,57 @@ int known_condition_true(struct expression *expr) show_special(expr->op), right); return ret; + +} + +struct statement *get_block_thing(struct expression *expr) +{ + /* What are those things called? if (({....; ret;})) { ...*/ + + if (expr->type != EXPR_PREOP) + return NULL; + if (expr->op != '(') + return NULL; + if (expr->unop->type != EXPR_STATEMENT) + return NULL; + if (expr->unop->statement->type != STMT_COMPOUND) + return NULL; + return expr->unop->statement; +} + +int block_thing_true(struct statement *stmt) +{ + struct expression *expr; + + stmt = last_ptr_list((struct ptr_list *)stmt->stmts); + if (stmt->type != STMT_EXPRESSION) + return 0; + expr = stmt->expression; + if (get_value(expr) == 1) + return 1; + return 0; +} + +int known_condition_true(struct expression *expr) +{ + if (!expr) + return 0; + + switch(expr->type) { + case EXPR_COMPARE: + return compare_true(expr); + case EXPR_PREOP: { + struct statement *stmt; + struct expression *tmp; + + stmt = get_block_thing(expr); + if (stmt) + return block_thing_true(stmt); + tmp = strip_expr(expr); + if (tmp != expr) + return known_condition_true(tmp); + break; + } + } + return 0; } diff --git a/smatch_helper.c b/smatch_helper.c index 2fea73ae..6d8dbe70 100644 --- a/smatch_helper.c +++ b/smatch_helper.c @@ -337,6 +337,9 @@ int get_value(struct expression *expr) int is_zero(struct expression *expr) { + if (!expr) + return 0; + if (expr->type == EXPR_VALUE && expr->value == 0) return 1; if (expr->op == '(') diff --git a/smatch_states.c b/smatch_states.c index 587f8582..fb5b2c6d 100644 --- a/smatch_states.c +++ b/smatch_states.c @@ -235,6 +235,28 @@ static void __use_cond_stack(struct state_list_stack **stack) push_slist(stack, slist); } +void __save_false_states_for_later() +{ + struct state_list *pre_conditions; + struct state_list *false_states; + struct state_list *tmp; + + pre_conditions = pop_slist(&pre_cond_stack); + false_states = pop_slist(&cond_false_stack); + tmp = clone_slist(pre_conditions); + + overwrite_slist(false_states, &tmp); + + push_slist(&pre_cond_stack, tmp); + push_slist(&pre_cond_stack, pre_conditions); + push_slist(&cond_false_stack, false_states); +} + +void __use_previously_stored_false_states() +{ + del_slist(&cur_slist); + cur_slist = pop_slist(&pre_cond_stack); +} void __use_cond_true_states() { diff --git a/validation/sm_select.c b/validation/sm_select.c new file mode 100644 index 00000000..fc9f04fc --- /dev/null +++ b/validation/sm_select.c @@ -0,0 +1,37 @@ +struct foo { + int a; +}; + +struct foo *a; +struct foo *b; + +struct foo *c; +struct foo *d; +struct foo *e; +void func (void) +{ + if (a?b:0) { + a->a = 1; + b->a = 1; + } + a->a = 1; + b->a = 1; + d = returns_nonnull(); + if (c?d:e) { + c->a = 1; + d->a = 1; + e->a = 1; + } +} + +/* + * check-name: Ternary Conditions + * check-command: smatch sm_select.c + * + * check-output-start +sm_select.c +17 func(6) Dereferencing Undefined: 'a' +sm_select.c +18 func(7) Dereferencing Undefined: 'b' +sm_select.c +21 func(10) Dereferencing Undefined: 'c' +sm_select.c +23 func(12) Dereferencing Undefined: 'e' + * check-output-end + */ -- 2.11.4.GIT