Powered by Linux
[PATCH] check_iterator: Warn about end of loop tests for list_for_each_entry — Semantic Matching Tool

[PATCH] check_iterator: Warn about end of loop tests for list_for_each_entry

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux