1 /*
2  * Copyright (C) 2010 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 /*
19  * According to an email on lkml you are not allowed to reuse the skb
20  * passed to dev_queue_xmit()
21  *
22  */
23 
24 #include "smatch.h"
25 #include "smatch_slist.h"
26 
27 static int my_id;
28 
29 STATE(do_not_use);
30 
ok_to_use(struct sm_state * sm,struct expression * mod_expr)31 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
32 {
33 	set_state(my_id, sm->name, sm->sym, &undefined);
34 }
35 
valid_use(void)36 static int valid_use(void)
37 {
38 	struct expression *tmp;
39 	int i = 0;
40 	int dot_ops = 0;
41 
42 	FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
43 		if (!i++)
44 			continue;
45 		if (tmp->type == EXPR_PREOP && tmp->op == '(')
46 			continue;
47 		if (tmp->op == '.' && !dot_ops++)
48 			continue;
49 //		if (tmp->type == EXPR_POSTOP)
50 //			return 1;
51 		if (tmp->type == EXPR_CALL && sym_name_is("kfree_skb", tmp->fn))
52 			return 1;
53 		return 0;
54 	} END_FOR_EACH_PTR_REVERSE(tmp);
55 	return 0;
56 }
57 
58 /* match symbol is expensive.  only turn it on after we match the xmit function */
59 static int match_symbol_active;
match_symbol(struct expression * expr)60 static void match_symbol(struct expression *expr)
61 {
62 	struct sm_state *sm;
63 	char *name;
64 
65 	sm = get_sm_state_expr(my_id, expr);
66 	if (!sm || !slist_has_state(sm->possible, &do_not_use))
67 		return;
68 	if (valid_use())
69 		return;
70 	name = expr_to_var(expr);
71 	sm_error("'%s' was already used up by dev_queue_xmit()", name);
72 	free_string(name);
73 }
74 
match_kfree_skb(const char * fn,struct expression * expr,void * param)75 static void match_kfree_skb(const char *fn, struct expression *expr, void *param)
76 {
77 	struct expression *arg;
78 
79 	arg = get_argument_from_call_expr(expr->args, 0);
80 	if (!arg)
81 		return;
82 	set_state_expr(my_id, arg, &undefined);
83 }
84 
match_xmit(const char * fn,struct expression * expr,void * param)85 static void match_xmit(const char *fn, struct expression *expr, void *param)
86 {
87 	struct expression *arg;
88 
89 	arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
90 	if (!arg)
91 		return;
92 	set_state_expr(my_id, arg, &do_not_use);
93 	if (!match_symbol_active++) {
94 		add_hook(&match_symbol, SYM_HOOK);
95 		add_function_hook("kfree_skb", &match_kfree_skb, NULL);
96 	}
97 }
98 
register_funcs_from_file(void)99 static void register_funcs_from_file(void)
100 {
101 	struct token *token;
102 	const char *func;
103 	int arg;
104 
105 	token = get_tokens_file("kernel.dev_queue_xmit");
106 	if (!token)
107 		return;
108 	if (token_type(token) != TOKEN_STREAMBEGIN)
109 		return;
110 	token = token->next;
111 	while (token_type(token) != TOKEN_STREAMEND) {
112 		if (token_type(token) != TOKEN_IDENT)
113 			return;
114 		func = show_ident(token->ident);
115 		token = token->next;
116 		if (token_type(token) != TOKEN_NUMBER)
117 			return;
118 		arg = atoi(token->number);
119 		add_function_hook(func, &match_xmit, INT_PTR(arg));
120 		token = token->next;
121 	}
122 	clear_token_alloc();
123 }
124 
check_dev_queue_xmit(int id)125 void check_dev_queue_xmit(int id)
126 {
127 	if (option_project != PROJ_KERNEL)
128 		return;
129 	my_id = id;
130 	add_modification_hook(my_id, ok_to_use);
131 	register_funcs_from_file();
132 }
133