11f5207b7SJohn Levon /* 21f5207b7SJohn Levon * Copyright (C) 2010 Dan Carpenter. 31f5207b7SJohn Levon * 41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or 51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License 61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2 71f5207b7SJohn Levon * of the License, or (at your option) any later version. 81f5207b7SJohn Levon * 91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful, 101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of 111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121f5207b7SJohn Levon * GNU General Public License for more details. 131f5207b7SJohn Levon * 141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License 151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 161f5207b7SJohn Levon */ 171f5207b7SJohn Levon 181f5207b7SJohn Levon /* 191f5207b7SJohn Levon * This is like check_deref_check.c except that it complains about code like: 201f5207b7SJohn Levon * if (a) 211f5207b7SJohn Levon * a->foo = 42; 221f5207b7SJohn Levon * a->bar = 7; 231f5207b7SJohn Levon * 241f5207b7SJohn Levon * Of course, Smatch has complained about these for forever but the problem is 251f5207b7SJohn Levon * the old scripts were too messy and complicated and generated too many false 261f5207b7SJohn Levon * positives. 271f5207b7SJohn Levon * 281f5207b7SJohn Levon * This check is supposed to be simpler because it only looks for one kind of 291f5207b7SJohn Levon * null dereference bug instead of every kind. It also gets rid of the false 301f5207b7SJohn Levon * positives caused by the checks that happen inside macros. 311f5207b7SJohn Levon * 321f5207b7SJohn Levon */ 331f5207b7SJohn Levon 341f5207b7SJohn Levon #include "smatch.h" 351f5207b7SJohn Levon #include "smatch_slist.h" 361f5207b7SJohn Levon #include "smatch_extra.h" 371f5207b7SJohn Levon 381f5207b7SJohn Levon static int my_id; 391f5207b7SJohn Levon 401f5207b7SJohn Levon STATE(null); 411f5207b7SJohn Levon STATE(ok); 421f5207b7SJohn Levon 431f5207b7SJohn Levon static void is_ok(struct sm_state *sm, struct expression *mod_expr) 441f5207b7SJohn Levon { 451f5207b7SJohn Levon set_state(my_id, sm->name, sm->sym, &ok); 461f5207b7SJohn Levon } 471f5207b7SJohn Levon 481f5207b7SJohn Levon static void check_dereference(struct expression *expr) 491f5207b7SJohn Levon { 501f5207b7SJohn Levon struct sm_state *sm; 511f5207b7SJohn Levon struct sm_state *tmp; 521f5207b7SJohn Levon 531f5207b7SJohn Levon if (__in_fake_assign) 541f5207b7SJohn Levon return; 551f5207b7SJohn Levon 561f5207b7SJohn Levon expr = strip_expr(expr); 571f5207b7SJohn Levon 581f5207b7SJohn Levon sm = get_sm_state_expr(my_id, expr); 591f5207b7SJohn Levon if (!sm) 601f5207b7SJohn Levon return; 611f5207b7SJohn Levon if (is_ignored(my_id, sm->name, sm->sym)) 621f5207b7SJohn Levon return; 631f5207b7SJohn Levon if (implied_not_equal(expr, 0)) 641f5207b7SJohn Levon return; 651f5207b7SJohn Levon 661f5207b7SJohn Levon FOR_EACH_PTR(sm->possible, tmp) { 671f5207b7SJohn Levon if (tmp->state == &merged) 681f5207b7SJohn Levon continue; 691f5207b7SJohn Levon if (tmp->state == &ok) 701f5207b7SJohn Levon continue; 711f5207b7SJohn Levon if (tmp->state == &null) { 721f5207b7SJohn Levon sm_error("we previously assumed '%s' could be null (see line %d)", 731f5207b7SJohn Levon tmp->name, tmp->line); 741f5207b7SJohn Levon add_ignore(my_id, sm->name, sm->sym); 751f5207b7SJohn Levon return; 761f5207b7SJohn Levon } 771f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp); 781f5207b7SJohn Levon } 791f5207b7SJohn Levon 801f5207b7SJohn Levon static void check_dereference_name_sym(char *name, struct symbol *sym) 811f5207b7SJohn Levon { 821f5207b7SJohn Levon struct sm_state *sm; 831f5207b7SJohn Levon struct sm_state *tmp; 841f5207b7SJohn Levon 851f5207b7SJohn Levon sm = get_sm_state(my_id, name, sym); 861f5207b7SJohn Levon if (!sm) 871f5207b7SJohn Levon return; 881f5207b7SJohn Levon if (is_ignored(my_id, sm->name, sm->sym)) 891f5207b7SJohn Levon return; 901f5207b7SJohn Levon if (implied_not_equal_name_sym(name, sym, 0)) 911f5207b7SJohn Levon return; 921f5207b7SJohn Levon 931f5207b7SJohn Levon FOR_EACH_PTR(sm->possible, tmp) { 941f5207b7SJohn Levon if (tmp->state == &merged) 951f5207b7SJohn Levon continue; 961f5207b7SJohn Levon if (tmp->state == &ok) 971f5207b7SJohn Levon continue; 981f5207b7SJohn Levon if (tmp->state == &null) { 991f5207b7SJohn Levon sm_error("we previously assumed '%s' could be null (see line %d)", 1001f5207b7SJohn Levon tmp->name, tmp->line); 1011f5207b7SJohn Levon add_ignore(my_id, sm->name, sm->sym); 1021f5207b7SJohn Levon return; 1031f5207b7SJohn Levon } 1041f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp); 1051f5207b7SJohn Levon } 1061f5207b7SJohn Levon 1071f5207b7SJohn Levon static void match_dereferences(struct expression *expr) 1081f5207b7SJohn Levon { 1091f5207b7SJohn Levon if (expr->type != EXPR_PREOP) 1101f5207b7SJohn Levon return; 1111f5207b7SJohn Levon check_dereference(expr->unop); 1121f5207b7SJohn Levon } 1131f5207b7SJohn Levon 1141f5207b7SJohn Levon static void match_pointer_as_array(struct expression *expr) 1151f5207b7SJohn Levon { 1161f5207b7SJohn Levon if (!is_array(expr)) 1171f5207b7SJohn Levon return; 1181f5207b7SJohn Levon check_dereference(get_array_base(expr)); 1191f5207b7SJohn Levon } 1201f5207b7SJohn Levon 1211f5207b7SJohn Levon static void set_param_dereferenced(struct expression *call, struct expression *arg, char *key, char *unused) 1221f5207b7SJohn Levon { 1231f5207b7SJohn Levon struct symbol *sym; 1241f5207b7SJohn Levon char *name; 1251f5207b7SJohn Levon 1261f5207b7SJohn Levon name = get_variable_from_key(arg, key, &sym); 1271f5207b7SJohn Levon if (!name || !sym) 1281f5207b7SJohn Levon goto free; 1291f5207b7SJohn Levon 1301f5207b7SJohn Levon check_dereference_name_sym(name, sym); 1311f5207b7SJohn Levon free: 1321f5207b7SJohn Levon free_string(name); 1331f5207b7SJohn Levon } 1341f5207b7SJohn Levon 1351f5207b7SJohn Levon static void match_condition(struct expression *expr) 1361f5207b7SJohn Levon { 1371f5207b7SJohn Levon struct smatch_state *true_state = NULL; 138*c85f09ccSJohn Levon char *name; 1391f5207b7SJohn Levon 140*c85f09ccSJohn Levon name = get_macro_name(expr->pos); 141*c85f09ccSJohn Levon if (name && 142*c85f09ccSJohn Levon (strcmp(name, "likely") != 0 && strcmp(name, "unlikely") != 0)) 1431f5207b7SJohn Levon return; 1441f5207b7SJohn Levon 1451f5207b7SJohn Levon if (!is_pointer(expr)) 1461f5207b7SJohn Levon return; 1471f5207b7SJohn Levon 1481f5207b7SJohn Levon if (expr->type == EXPR_ASSIGNMENT) { 1491f5207b7SJohn Levon match_condition(expr->right); 1501f5207b7SJohn Levon match_condition(expr->left); 1511f5207b7SJohn Levon } 1521f5207b7SJohn Levon 1531f5207b7SJohn Levon if (implied_not_equal(expr, 0)) 1541f5207b7SJohn Levon return; 1551f5207b7SJohn Levon 1561f5207b7SJohn Levon if (get_state_expr(my_id, expr)) 1571f5207b7SJohn Levon true_state = &ok; 1581f5207b7SJohn Levon 1591f5207b7SJohn Levon set_true_false_states_expr(my_id, expr, true_state, &null); 1601f5207b7SJohn Levon } 1611f5207b7SJohn Levon 1621f5207b7SJohn Levon void check_check_deref(int id) 1631f5207b7SJohn Levon { 1641f5207b7SJohn Levon my_id = id; 1651f5207b7SJohn Levon 1661f5207b7SJohn Levon add_modification_hook(my_id, &is_ok); 1671f5207b7SJohn Levon add_hook(&match_dereferences, DEREF_HOOK); 1681f5207b7SJohn Levon add_hook(&match_pointer_as_array, OP_HOOK); 1691f5207b7SJohn Levon select_return_implies_hook(DEREFERENCE, &set_param_dereferenced); 1701f5207b7SJohn Levon add_hook(&match_condition, CONDITION_HOOK); 1711f5207b7SJohn Levon } 172