1 #include "smatch.h"
2 #include "smatch_slist.h"
3 
4 static int my_id;
5 
6 /* If set, we ignore struct type symbols as implicit dependencies */
7 static int ignore_structs;
8 
9 static struct symbol *cur_syscall;
10 /* note: cannot track return type and remove from implicit dependencies,
11  * because every syscall returns a long, and we don't have a good way to know
12  * whether or not this is a resource. The only example I can think of is open
13  * returning a filedescriptor, so in the implicit dep parsing, we will just
14  * blacklist struct fd --> file
15  */
16 static struct symbol *cur_return_type;
17 static char *syscall_name;
18 
19 static struct tracker_list *read_list;	// what fields does syscall branch on?
20 static struct tracker_list *write_list; // what fields does syscall modify?
21 static struct tracker_list *arg_list;	// what struct arguments does the syscall take?
22 static struct tracker_list *parsed_syscalls; // syscalls we have already checked
23 
prefix(void)24 static inline void prefix(void)
25 {
26 	printf("%s:%d %s() ", get_filename(), get_lineno(), get_function());
27 }
28 
match_syscall_definition(struct symbol * sym)29 static void match_syscall_definition(struct symbol *sym)
30 {
31 	struct symbol *arg;
32 	struct tracker *tracker;
33 	char *macro;
34 	char *name;
35 	int is_syscall = 0;
36 
37 	macro = get_macro_name(sym->pos);
38 	if (macro &&
39 	    (strncmp("SYSCALL_DEFINE", macro, strlen("SYSCALL_DEFINE")) == 0 ||
40 	     strncmp("COMPAT_SYSCALL_DEFINE", macro, strlen("COMPAT_SYSCALL_DEFINE")) == 0))
41 		is_syscall = 1;
42 
43 	name = get_function();
44 
45 	if (name && strncmp(name, "sys_", 4) == 0)
46 		is_syscall = 1;
47 
48 	if (name && strncmp(name, "compat_sys_", 11) == 0)
49 		is_syscall = 1;
50 
51 	if (!is_syscall)
52 		return;
53 
54 	FOR_EACH_PTR(parsed_syscalls, tracker) {
55 		if (tracker->sym == sym) // don't re-parse
56 			return;
57 	} END_FOR_EACH_PTR(tracker);
58 
59 	syscall_name = name;
60 	cur_syscall = sym;
61 
62 	cur_return_type = cur_func_return_type();
63 	if (cur_return_type && cur_return_type->ident)
64 		sm_msg("return type: %s\n", cur_return_type->ident->name);
65 
66 
67 	FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
68 		// set_state(my_id, arg->ident->name, arg, &user_data_set);
69 		sm_msg("=======check_impl: arguments for call %s=========\n", syscall_name);
70 		if (arg->type == SYM_STRUCT)
71 			arg = get_real_base_type(arg);
72 		if (cur_return_type && cur_return_type->ident)
73 			sm_msg("arg type: %s\n", cur_return_type->ident->name);
74 		// add_tracker(&arg_list, my_id, member, arg);
75 		sm_msg("=================================\n");
76 	} END_FOR_EACH_PTR(arg);
77 }
78 
print_read_list(void)79 static void print_read_list(void)
80 {
81     struct tracker *tracker;
82     int i = 0;
83 
84     FOR_EACH_PTR(read_list, tracker) {
85 	    if (i == 0)
86 		    sm_printf("%s read_list: [", syscall_name);
87 	    sm_printf("%s, ", tracker->name);
88 	    i++;
89     } END_FOR_EACH_PTR(tracker);
90 
91     if (i > 0)
92 	    sm_printf("]\n");
93 }
94 
print_write_list(void)95 static void print_write_list(void)
96 {
97 	struct tracker *tracker;
98 	int i = 0;
99 
100 	FOR_EACH_PTR(write_list, tracker) {
101 		if (i == 0)
102 			sm_printf("%s write_list: [", syscall_name);
103 		sm_printf("%s, ", tracker->name);
104 		i++;
105 	} END_FOR_EACH_PTR(tracker);
106 
107 	if (i > 0)
108 		sm_printf("]\n");
109 }
110 
print_arg_list(void)111 static void print_arg_list(void)
112 {
113 	struct tracker *tracker;
114 	int i = 0;
115 
116 	FOR_EACH_PTR(write_list, tracker) {
117 		if (i == 0)
118 			sm_printf("%s arg_list: [", syscall_name);
119 		sm_printf("%s, ", tracker->name);
120 		i++;
121 	} END_FOR_EACH_PTR(tracker);
122 
123 	if (i > 0)
124 		sm_printf("]\n");
125 }
126 
match_after_syscall(struct symbol * sym)127 static void match_after_syscall(struct symbol *sym)
128 {
129 	if (!cur_syscall || sym != cur_syscall)
130 		return;
131 	// printf("\n"); prefix();
132 	// printf("exiting scope of syscall %s\n", get_function());
133 	// printf("-------------------------\n");
134 	print_read_list();
135 	print_write_list();
136 	print_arg_list();
137 	free_trackers_and_list(&read_list);
138 	free_trackers_and_list(&write_list);
139 	free_trackers_and_list(&arg_list);
140 	add_tracker(&parsed_syscalls, my_id, syscall_name, sym);
141 	cur_syscall = NULL;
142 	cur_return_type = NULL;
143 	syscall_name = NULL;
144 }
145 
print_read_member_type(struct expression * expr)146 static void print_read_member_type(struct expression *expr)
147 {
148 	char *member;
149 	struct symbol *sym;
150 	struct symbol *member_sym;
151 
152 	member = get_member_name(expr);
153 	if (!member)
154 		return;
155 
156 	sym = get_type(expr->deref);
157 	member_sym = get_type(expr);
158 
159 	if (member_sym->type == SYM_PTR)
160 		member_sym = get_real_base_type(member_sym);
161 
162 	/*
163 	if (member_sym->type == SYM_STRUCT)
164 		printf("found struct type %s\n", member);
165 	else
166 		printf("found non-struct type %s with enum value%d\n", member, member_sym->type);
167 	*/
168 
169 	if (ignore_structs && member_sym->type == SYM_STRUCT) {
170 		// printf("ignoring %s\n", member);
171 		return;
172 	}
173 
174 	add_tracker(&read_list, my_id, member, sym);
175 	// sm_msg("info: uses %s", member);
176 	// prefix();
177 	// printf("info: uses %s\n", member);
178 	free_string(member);
179 }
180 
print_write_member_type(struct expression * expr)181 static void print_write_member_type(struct expression *expr)
182 {
183 	char *member;
184 	struct symbol *sym;
185 	struct symbol *member_sym;
186 
187 	member = get_member_name(expr);
188 	if (!member)
189 		return;
190 
191 	sym = get_type(expr->deref);
192 	member_sym = get_type(expr);
193 
194 	if (member_sym->type == SYM_PTR)
195 		member_sym = get_real_base_type(member_sym);
196 
197 	/*
198 	if (member_sym->type == SYM_STRUCT)
199 		printf("found struct type %s\n", member);
200 	else
201 		printf("found non-struct type %s with enum value%d\n", member, member_sym->type);
202 	*/
203 
204 	if (ignore_structs && member_sym->type == SYM_STRUCT) {
205 		// printf("ignoring %s\n", member);
206 		return;
207 	}
208 
209 	add_tracker(&write_list, my_id, member, sym);
210 	free_string(member);
211 }
212 
match_condition(struct expression * expr)213 static void match_condition(struct expression *expr)
214 {
215 	struct expression *arg;
216 
217 	if (!cur_syscall)
218 		return;
219 
220 	// prefix(); printf("-- condition found\n");
221 
222 	if (expr->type == EXPR_COMPARE ||
223 	    expr->type == EXPR_BINOP ||
224 	    expr->type == EXPR_LOGICAL ||
225 	    expr->type == EXPR_ASSIGNMENT ||
226 	    expr->type == EXPR_COMMA) {
227 		match_condition(expr->left);
228 		match_condition(expr->right);
229 		return;
230 	}
231 
232 	if (expr->type == EXPR_CALL) {
233 		FOR_EACH_PTR(expr->args, arg) {
234 			// if we find deref in conditional call,
235 			// mark it as a read dependency
236 			print_read_member_type(arg);
237 		} END_FOR_EACH_PTR(arg);
238 		return;
239 	}
240 
241 	print_read_member_type(expr);
242 }
243 
244 
245 /* when we are parsing an inline function and can no longer nest,
246  * assume that all struct fields passed to nested inline functions
247  * are read dependencies
248  */
match_call_info(struct expression * expr)249 static void match_call_info(struct expression *expr)
250 {
251 	struct expression *arg;
252 	int i;
253 
254 	if (!__inline_fn || !cur_syscall)
255 		return;
256 
257 	// prefix(); printf("fn: %s\n", expr->fn->symbol->ident->name);
258 
259 	i = 0;
260 	FOR_EACH_PTR(expr->args, arg) {
261 		/*
262 		   if (arg->type == EXPR_DEREF)
263 		   printf("arg %d is deref\n", i);
264 		 */
265 		print_read_member_type(arg);
266 		i++;
267 	} END_FOR_EACH_PTR(arg);
268 }
269 
match_assign_value(struct expression * expr)270 static void match_assign_value(struct expression *expr)
271 {
272 	if (!cur_syscall)
273 		return;
274 	print_write_member_type(expr->left);
275 }
276 
unop_expr(struct expression * expr)277 static void unop_expr(struct expression *expr)
278 {
279 	if (!cur_syscall)
280 		return;
281 
282 	if (expr->op == SPECIAL_ADD_ASSIGN || expr->op == SPECIAL_INCREMENT ||
283 	    expr->op == SPECIAL_SUB_ASSIGN || expr->op == SPECIAL_DECREMENT ||
284 	    expr->op == SPECIAL_MUL_ASSIGN || expr->op == SPECIAL_DIV_ASSIGN ||
285 	    expr->op == SPECIAL_MOD_ASSIGN || expr->op == SPECIAL_AND_ASSIGN ||
286 	    expr->op == SPECIAL_OR_ASSIGN || expr->op == SPECIAL_XOR_ASSIGN ||
287 	    expr->op == SPECIAL_SHL_ASSIGN || expr->op == SPECIAL_SHR_ASSIGN)
288 		print_write_member_type(strip_expr(expr->unop));
289 }
290 
check_implicit_dependencies(int id)291 void check_implicit_dependencies(int id)
292 {
293 	my_id = id;
294 	ignore_structs = 0;
295 
296 	if (option_project != PROJ_KERNEL)
297 		return;
298 	if (!option_info)
299 		return;
300 
301 	add_hook(&match_syscall_definition, AFTER_DEF_HOOK);
302 	add_hook(&match_after_syscall, AFTER_FUNC_HOOK);
303 	add_hook(&match_condition, CONDITION_HOOK);
304 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
305 
306 	/* hooks to track written fields */
307 	add_hook(&match_assign_value, ASSIGNMENT_HOOK_AFTER);
308 	add_hook(&unop_expr, OP_HOOK);
309 }
310