1 /* 2 * Copyright (C) 2009 Dan Carpenter. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16 */ 17 18 #include "smatch.h" 19 #include "smatch_slist.h" 20 #include "smatch_extra.h" 21 22 static int my_id; 23 24 STATE(err_ptr); 25 STATE(checked); 26 27 static sval_t err_ptr_min = { 28 .type = &int_ctype, 29 {.value = -4095}, 30 }; 31 32 static sval_t err_ptr_max = { 33 .type = &int_ctype, 34 {.value = -1}, 35 }; 36 37 struct range_list *err_ptr_rl; 38 39 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) 40 { 41 if (sm->state != &checked) 42 set_state(my_id, sm->name, sm->sym, &checked); 43 } 44 45 static void check_is_err_ptr(struct expression *expr) 46 { 47 struct sm_state *sm; 48 struct range_list *rl; 49 50 sm = get_sm_state_expr(my_id, expr); 51 if (!sm) 52 return; 53 54 if (!slist_has_state(sm->possible, &err_ptr)) 55 return; 56 57 get_absolute_rl(expr, &rl); 58 if (!possibly_true_rl(rl, SPECIAL_EQUAL, err_ptr_rl)) 59 return; 60 61 sm_error("'%s' dereferencing possible ERR_PTR()", sm->name); 62 set_state(my_id, sm->name, sm->sym, &checked); 63 } 64 65 static void match_returns_err_ptr(const char *fn, struct expression *expr, 66 void *info) 67 { 68 set_state_expr(my_id, expr->left, &err_ptr); 69 } 70 71 static void set_param_dereferenced(struct expression *call, struct expression *arg, char *key, char *unused) 72 { 73 struct sm_state *sm; 74 struct smatch_state *estate; 75 struct symbol *sym; 76 char *name; 77 78 name = get_variable_from_key(arg, key, &sym); 79 if (!name || !sym) 80 goto free; 81 82 sm = get_sm_state(my_id, name, sym); 83 if (!sm) 84 goto free; 85 86 if (!slist_has_state(sm->possible, &err_ptr)) 87 goto free; 88 89 estate = get_state(SMATCH_EXTRA, name, sym); 90 if (!estate || !possibly_true_rl(estate_rl(estate), SPECIAL_EQUAL, err_ptr_rl)) 91 goto free; 92 93 sm_error("'%s' dereferencing possible ERR_PTR()", sm->name); 94 set_state(my_id, sm->name, sm->sym, &checked); 95 96 free: 97 free_string(name); 98 } 99 100 static void match_checked(const char *fn, struct expression *call_expr, 101 struct expression *assign_expr, void *unused) 102 { 103 struct expression *arg; 104 105 arg = get_argument_from_call_expr(call_expr->args, 0); 106 arg = strip_expr(arg); 107 while (arg->type == EXPR_ASSIGNMENT) 108 arg = strip_expr(arg->left); 109 set_state_expr(my_id, arg, &checked); 110 } 111 112 static void match_err(const char *fn, struct expression *call_expr, 113 struct expression *assign_expr, void *unused) 114 { 115 struct expression *arg; 116 117 arg = get_argument_from_call_expr(call_expr->args, 0); 118 arg = strip_expr(arg); 119 while (arg->type == EXPR_ASSIGNMENT) 120 arg = strip_expr(arg->left); 121 set_state_expr(my_id, arg, &err_ptr); 122 } 123 124 static void match_dereferences(struct expression *expr) 125 { 126 if (expr->type != EXPR_PREOP) 127 return; 128 check_is_err_ptr(expr->unop); 129 } 130 131 static void match_kfree(const char *fn, struct expression *expr, void *_arg_nr) 132 { 133 int arg_nr = PTR_INT(_arg_nr); 134 struct expression *arg; 135 136 arg = get_argument_from_call_expr(expr->args, arg_nr); 137 check_is_err_ptr(arg); 138 } 139 140 static void match_condition(struct expression *expr) 141 { 142 if (expr->type == EXPR_ASSIGNMENT) { 143 match_condition(expr->right); 144 match_condition(expr->left); 145 } 146 if (!get_state_expr(my_id, expr)) 147 return; 148 /* If we know the variable is zero that means it's not an ERR_PTR */ 149 set_true_false_states_expr(my_id, expr, NULL, &checked); 150 } 151 152 static void register_err_ptr_funcs(void) 153 { 154 struct token *token; 155 const char *func; 156 157 token = get_tokens_file("kernel.returns_err_ptr"); 158 if (!token) 159 return; 160 if (token_type(token) != TOKEN_STREAMBEGIN) 161 return; 162 token = token->next; 163 while (token_type(token) != TOKEN_STREAMEND) { 164 if (token_type(token) != TOKEN_IDENT) 165 return; 166 func = show_ident(token->ident); 167 add_function_assign_hook(func, &match_returns_err_ptr, NULL); 168 token = token->next; 169 } 170 clear_token_alloc(); 171 } 172 173 static void match_err_ptr_positive_const(const char *fn, struct expression *expr, void *unused) 174 { 175 struct expression *arg; 176 sval_t sval; 177 178 arg = get_argument_from_call_expr(expr->args, 0); 179 180 if (!get_value(arg, &sval)) 181 return; 182 if (sval_is_positive(sval) && sval_cmp_val(sval, 0) != 0) 183 sm_error("passing non negative %s to ERR_PTR", sval_to_str(sval)); 184 } 185 186 static void match_err_ptr(const char *fn, struct expression *expr, void *unused) 187 { 188 struct expression *arg; 189 struct sm_state *sm; 190 struct sm_state *tmp; 191 sval_t tmp_min; 192 sval_t tmp_max; 193 sval_t min = sval_type_max(&llong_ctype); 194 sval_t max = sval_type_min(&llong_ctype); 195 196 arg = get_argument_from_call_expr(expr->args, 0); 197 sm = get_sm_state_expr(SMATCH_EXTRA, arg); 198 if (!sm) 199 return; 200 FOR_EACH_PTR(sm->possible, tmp) { 201 tmp_min = estate_min(tmp->state); 202 if (!sval_is_a_min(tmp_min) && sval_cmp(tmp_min, min) < 0) 203 min = tmp_min; 204 tmp_max = estate_max(tmp->state); 205 if (!sval_is_a_max(tmp_max) && sval_cmp(tmp_max, max) > 0) 206 max = tmp_max; 207 } END_FOR_EACH_PTR(tmp); 208 if (sval_is_negative(min) && sval_cmp_val(min, -4095) < 0) 209 sm_error("%s too low for ERR_PTR", sval_to_str(min)); 210 if (sval_is_positive(max) && sval_cmp_val(max, 0) != 0) 211 sm_error("passing non negative %s to ERR_PTR", sval_to_str(max)); 212 } 213 214 void check_err_ptr_deref(int id) 215 { 216 if (option_project != PROJ_KERNEL) 217 return; 218 219 my_id = id; 220 return_implies_state("IS_ERR", 0, 0, &match_checked, NULL); 221 return_implies_state("IS_ERR", 1, 1, &match_err, NULL); 222 return_implies_state("IS_ERR_OR_NULL", 0, 0, &match_checked, NULL); 223 return_implies_state("IS_ERR_OR_NULL", 1, 1, &match_err, NULL); 224 return_implies_state("PTR_RET", 0, 0, &match_checked, NULL); 225 return_implies_state("PTR_RET", -4095, -1, &match_err, NULL); 226 register_err_ptr_funcs(); 227 add_hook(&match_dereferences, DEREF_HOOK); 228 add_function_hook("ERR_PTR", &match_err_ptr_positive_const, NULL); 229 add_function_hook("ERR_PTR", &match_err_ptr, NULL); 230 add_hook(&match_condition, CONDITION_HOOK); 231 add_modification_hook(my_id, &ok_to_use); 232 add_function_hook("kfree", &match_kfree, INT_PTR(0)); 233 add_function_hook("brelse", &match_kfree, INT_PTR(0)); 234 add_function_hook("kmem_cache_free", &match_kfree, INT_PTR(1)); 235 add_function_hook("vfree", &match_kfree, INT_PTR(0)); 236 237 err_ptr_rl = clone_rl_permanent(alloc_rl(err_ptr_min, err_ptr_max)); 238 239 select_return_implies_hook(DEREFERENCE, &set_param_dereferenced); 240 } 241 242