2 * Copyright (C) 2020 Oracle.
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.
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.
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
19 * This check looks for code like:
30 * And the bug is that some error paths forgot to set "ret" to an error code.
35 #include "smatch_slist.h"
36 #include "smatch_extra.h"
40 static unsigned long set_label
;
42 static void match_goto(struct statement
*stmt
)
44 if (stmt
->type
!= STMT_GOTO
)
46 set_state(my_id
, "goto", NULL
, &true_state
);
49 static void match_label(struct statement
*stmt
)
53 if (stmt
->type
!= STMT_LABEL
)
55 if (get_state(my_id
, "cleanup", NULL
) == &true_state
) {
56 /* The second label in a cleanup block is still cleanup. */
62 * If the cleanup block isn't preceded by a "return 0;" then it could
66 if (__get_cur_stree())
68 if (!__prev_stmt
|| __prev_stmt
->type
!= STMT_RETURN
)
70 if (!get_implied_value(__prev_stmt
->ret_value
, &sval
) || sval
.value
!= 0)
72 /* We can't set the state until after we've merged the gotos. */
76 static void match_label_after(struct statement
*stmt
)
78 if (stmt
->type
!= STMT_LABEL
)
81 set_state(my_id
, "cleanup", NULL
, &true_state
);
86 static bool find_pool(struct sm_state
*extra_sm
, struct stree
*pool
)
88 if (!extra_sm
|| !pool
)
91 if (extra_sm
->pool
== pool
)
93 if (find_pool(extra_sm
->left
, pool
))
95 if (find_pool(extra_sm
->right
, pool
))
101 static int check_pool(struct sm_state
*goto_sm
, struct sm_state
*extra_sm
)
103 struct sm_state
*old
;
108 if (goto_sm
->merged
|| !goto_sm
->pool
)
111 orig
= __swap_cur_stree(goto_sm
->pool
);
113 old
= get_sm_state(SMATCH_EXTRA
, extra_sm
->name
, extra_sm
->sym
);
116 if (goto_sm
->line
< old
->line
)
117 goto swap
; // hopefully this is impossible
118 if (goto_sm
->line
< old
->line
+ 3)
119 goto swap
; // the ret value is deliberately set to zero
120 if (!estate_get_single_value(old
->state
, &sval
) || sval
.value
!= 0)
121 goto swap
; // the ret variable is not zero
122 if (!find_pool(extra_sm
, old
->pool
))
123 goto swap
; // the ret variable was modified after the goto
125 line
= goto_sm
->line
;
127 __swap_cur_stree(orig
);
131 static int recursive_search_for_zero(struct sm_state
*goto_sm
, struct sm_state
*extra_sm
)
138 line
= check_pool(goto_sm
, extra_sm
);
142 line
= recursive_search_for_zero(goto_sm
->left
, extra_sm
);
145 line
= recursive_search_for_zero(goto_sm
->right
, extra_sm
);
152 static void match_return(struct statement
*stmt
)
154 struct sm_state
*goto_sm
, *extra_sm
;
158 if (stmt
->type
!= STMT_RETURN
)
160 if (!stmt
->ret_value
|| stmt
->ret_value
->type
!= EXPR_SYMBOL
)
162 if (cur_func_return_type() != &int_ctype
)
164 if (get_state(my_id
, "cleanup", NULL
) != &true_state
)
166 goto_sm
= get_goto_sm_state();
167 if (!goto_sm
|| goto_sm
->state
!= &true_state
)
169 extra_sm
= get_extra_sm_state(stmt
->ret_value
);
172 if (!estate_rl(extra_sm
->state
) ||
173 !sval_is_negative(rl_min(estate_rl(extra_sm
->state
))))
175 line
= recursive_search_for_zero(goto_sm
, extra_sm
);
179 name
= expr_to_str(stmt
->ret_value
);
180 sm_printf("%s:%d %s() warn: missing error code '%s'\n",
181 get_filename(), line
, get_function(), name
);
185 void check_missing_error_code(int id
)
187 if (option_project
!= PROJ_KERNEL
)
192 add_function_data(&set_label
);
194 add_hook(&match_goto
, STMT_HOOK
);
195 add_hook(&match_label
, STMT_HOOK
);
196 add_hook(&match_label_after
, STMT_HOOK_AFTER
);
197 add_hook(&match_return
, STMT_HOOK
);