id is supposed to be unsigned short.
[smatch.git] / check_err_ptr_deref.c
blob45ac0787e8d50dbf1299f064f83b6a2b3b8cf597
1 /*
2 * sparse/check_err_ptr_deref.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include "smatch.h"
11 #include "smatch_slist.h"
12 #include "smatch_extra.h"
14 static int my_id;
16 STATE(err_ptr);
17 STATE(checked);
19 static void check_is_err_ptr(struct sm_state *sm)
21 if (!sm)
22 return;
24 if (slist_has_state(sm->possible, &err_ptr)) {
25 sm_msg("error: '%s' dereferencing possible ERR_PTR()",
26 sm->name);
27 set_state(my_id, sm->name, sm->sym, &checked);
31 static void match_returns_err_ptr(const char *fn, struct expression *expr,
32 void *info)
34 set_state_expr(my_id, expr->left, &err_ptr);
37 static void match_is_err(const char *fn, struct expression *expr,
38 void *data)
40 expr = get_argument_from_call_expr(expr->args, 0);
41 if (expr->type == EXPR_ASSIGNMENT)
42 expr = expr->left;
43 set_true_false_states_expr(my_id, expr, &err_ptr, &checked);
46 static void match_dereferences(struct expression *expr)
48 struct sm_state *sm;
50 if (expr->type == EXPR_PREOP) {
51 expr = strip_expr(expr->unop);
52 } else {
53 expr = strip_expr(expr->deref->unop);
56 sm = get_sm_state_expr(my_id, expr);
57 check_is_err_ptr(sm);
60 static void register_err_ptr_funcs(void)
62 struct token *token;
63 const char *func;
65 token = get_tokens_file("kernel.returns_err_ptr");
66 if (!token)
67 return;
68 if (token_type(token) != TOKEN_STREAMBEGIN)
69 return;
70 token = token->next;
71 while (token_type(token) != TOKEN_STREAMEND) {
72 if (token_type(token) != TOKEN_IDENT)
73 return;
74 func = show_ident(token->ident);
75 add_function_assign_hook(func, &match_returns_err_ptr, NULL);
76 token = token->next;
78 clear_token_alloc();
81 static void match_err_ptr(const char *fn, struct expression *expr, void *unused)
83 struct expression *arg;
84 struct sm_state *sm;
85 struct sm_state *tmp;
86 long long tmp_min;
87 long long tmp_max;
88 long long min = whole_range.max;
89 long long max = whole_range.min;
91 arg = get_argument_from_call_expr(expr->args, 0);
92 sm = get_sm_state_expr(SMATCH_EXTRA, arg);
93 if (!sm)
94 return;
95 FOR_EACH_PTR(sm->possible, tmp) {
96 tmp_min = get_dinfo_min((struct data_info *)tmp->state->data);
97 if (tmp_min != whole_range.min && tmp_min < min)
98 min = tmp_min;
99 tmp_max = get_dinfo_max((struct data_info *)tmp->state->data);
100 if (tmp_max != whole_range.max && tmp_max > max)
101 max = tmp_max;
102 } END_FOR_EACH_PTR(tmp);
103 if (min < -4095)
104 sm_msg("error: %lld too low for ERR_PTR", min);
105 if (max > 0)
106 sm_msg("error: passing non neg %lld to ERR_PTR", max);
109 static void match_ptr_err(const char *fn, struct expression *expr, void *unused)
111 struct expression *arg;
112 struct expression *right;
114 right = strip_expr(expr->right);
115 arg = get_argument_from_call_expr(right->args, 0);
116 if (get_state_expr(my_id, arg) == &err_ptr) {
117 set_state_expr(SMATCH_EXTRA, expr->left, alloc_extra_state_range(-4095, -1));
121 void check_err_ptr_deref(int id)
123 if (option_project != PROJ_KERNEL)
124 return;
126 my_id = id;
127 set_default_state(my_id, &checked);
128 add_conditional_hook("IS_ERR", &match_is_err, NULL);
129 register_err_ptr_funcs();
130 add_hook(&match_dereferences, DEREF_HOOK);
131 add_function_hook("ERR_PTR", &match_err_ptr, NULL);
132 add_function_assign_hook("PTR_ERR", &match_ptr_err, NULL);