The list_for_each_entry() iterator can never be NULL. If we exit the loop without finding the correct value then the iterator points invalid memory that is an offset from the list head. This will eventually lead to memory corruption and presumably a kernel crash. This patch sends warnings when such a code is encountered in the kernel. Signed-off-by: Harshvardhan Jha <harshvardhan.jha@xxxxxxxxxx> --- check_iterator_list_for_each.c | 79 ++++++++++++++++++++++++++++++++++ check_list.h | 2 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 check_iterator_list_for_each.c diff --git a/check_iterator_list_for_each.c b/check_iterator_list_for_each.c new file mode 100644 index 00000000..c6f808c9 --- /dev/null +++ b/check_iterator_list_for_each.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt + */ + +#include "smatch.h" +#include "smatch_slist.h" +#include "smatch_extra.h" + +static int my_id; + +STATE(iterator_list_for_each); + +static bool is_list_for_each(struct position pos) +{ + char *macro; + + macro = get_macro_name(pos); + if (!macro || strncmp(macro, "list_for_each", strlen("list_for_each")) != 0) + return false; + return true; +} + +static void set_undefined(struct sm_state *sm, struct expression *mod_expr) +{ + set_state(my_id, sm->name, sm->sym, &undefined); +} + +static void match_statement(struct statement *stmt) +{ + struct expression *expr; + + if (stmt->type != STMT_ITERATOR) + return; + + if (!stmt->iterator_pre_statement || + !stmt->iterator_pre_condition || + !stmt->iterator_post_statement) + return; + + if (stmt->iterator_post_statement->type != STMT_EXPRESSION) + return; + + expr = stmt->iterator_post_statement->expression; + if (expr->type != EXPR_ASSIGNMENT) + return; + + if (is_list_for_each(stmt->pos)) + set_state_expr(my_id, expr->left, &iterator_list_for_each); +} + +static void match_condition(struct expression *expr) +{ + if (get_state_expr(my_id, expr) != &iterator_list_for_each) + return; + // if (possibly_true(expr, SPECIAL_EQUAL, zero_expr())) + // return; + sm_warning("iterator(latest) '%s' cannot be NULL", expr_to_str(expr)); +} + +void check_iterator_list_for_each(int id) +{ + my_id = id; + add_modification_hook(my_id, &set_undefined); + add_hook(&match_statement, STMT_HOOK_AFTER); + add_hook(&match_condition, CONDITION_HOOK); +} diff --git a/check_list.h b/check_list.h index fd205269..2a84c945 100644 --- a/check_list.h +++ b/check_list.h @@ -169,7 +169,7 @@ CK(check_atomic_test) CK(check_checking_for_null_instead_of_err_ptr) CK(check_syscall_arg_type) CK(check_trinity_generator) - +CK(check_iterator_list_for_each) /* <- your test goes here */ /* CK(register_template) */ -- 2.32.0