xref: /illumos-gate/usr/src/tools/smatch/src/sparse.c (revision 856f710c9dc323b39da5935194d7928ffb99b67f)
1 /*
2  * Example trivial client program that uses the sparse library
3  * to tokenize, preprocess and parse a C file, and prints out
4  * the results.
5  *
6  * Copyright (C) 2003 Transmeta Corp.
7  *               2003-2004 Linus Torvalds
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 
35 #include "lib.h"
36 #include "allocate.h"
37 #include "token.h"
38 #include "parse.h"
39 #include "symbol.h"
40 #include "expression.h"
41 #include "linearize.h"
42 
43 static int context_increase(struct basic_block *bb, int entry)
44 {
45 	int sum = 0;
46 	struct instruction *insn;
47 
48 	FOR_EACH_PTR(bb->insns, insn) {
49 		int val;
50 		if (insn->opcode != OP_CONTEXT)
51 			continue;
52 		val = insn->increment;
53 		if (insn->check) {
54 			int current = sum + entry;
55 			if (!val) {
56 				if (!current)
57 					continue;
58 			} else if (current >= val)
59 				continue;
60 			warning(insn->pos, "context check failure");
61 			continue;
62 		}
63 		sum += val;
64 	} END_FOR_EACH_PTR(insn);
65 	return sum;
66 }
67 
68 static int imbalance(struct entrypoint *ep, struct basic_block *bb, int entry, int exit, const char *why)
69 {
70 	if (Wcontext) {
71 		struct symbol *sym = ep->name;
72 		warning(bb->pos, "context imbalance in '%s' - %s", show_ident(sym->ident), why);
73 	}
74 	return -1;
75 }
76 
77 static int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit);
78 
79 static int check_children(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
80 {
81 	struct instruction *insn;
82 	struct basic_block *child;
83 
84 	insn = last_instruction(bb->insns);
85 	if (!insn)
86 		return 0;
87 	if (insn->opcode == OP_RET)
88 		return entry != exit ? imbalance(ep, bb, entry, exit, "wrong count at exit") : 0;
89 
90 	FOR_EACH_PTR(bb->children, child) {
91 		if (check_bb_context(ep, child, entry, exit))
92 			return -1;
93 	} END_FOR_EACH_PTR(child);
94 	return 0;
95 }
96 
97 static int check_bb_context(struct entrypoint *ep, struct basic_block *bb, int entry, int exit)
98 {
99 	if (!bb)
100 		return 0;
101 	if (bb->context == entry)
102 		return 0;
103 
104 	/* Now that's not good.. */
105 	if (bb->context >= 0)
106 		return imbalance(ep, bb, entry, bb->context, "different lock contexts for basic block");
107 
108 	bb->context = entry;
109 	entry += context_increase(bb, entry);
110 	if (entry < 0)
111 		return imbalance(ep, bb, entry, exit, "unexpected unlock");
112 
113 	return check_children(ep, bb, entry, exit);
114 }
115 
116 static void check_cast_instruction(struct instruction *insn)
117 {
118 	struct symbol *orig_type = insn->orig_type;
119 	if (orig_type) {
120 		int old = orig_type->bit_size;
121 		int new = insn->size;
122 		int oldsigned = (orig_type->ctype.modifiers & MOD_SIGNED) != 0;
123 		int newsigned = insn->opcode == OP_SCAST;
124 
125 		if (new > old) {
126 			if (oldsigned == newsigned)
127 				return;
128 			if (newsigned)
129 				return;
130 			warning(insn->pos, "cast loses sign");
131 			return;
132 		}
133 		if (new < old) {
134 			warning(insn->pos, "cast drops bits");
135 			return;
136 		}
137 		if (oldsigned == newsigned) {
138 			warning(insn->pos, "cast wasn't removed");
139 			return;
140 		}
141 		warning(insn->pos, "cast changes sign");
142 	}
143 }
144 
145 static void check_range_instruction(struct instruction *insn)
146 {
147 	warning(insn->pos, "value out of range");
148 }
149 
150 static void check_byte_count(struct instruction *insn, pseudo_t count)
151 {
152 	if (!count)
153 		return;
154 	if (count->type == PSEUDO_VAL) {
155 		unsigned long long val = count->value;
156 		if (Wmemcpy_max_count && val > fmemcpy_max_count)
157 			warning(insn->pos, "%s with byte count of %llu",
158 				show_ident(insn->func->sym->ident), val);
159 		return;
160 	}
161 	/* OK, we could try to do the range analysis here */
162 }
163 
164 static pseudo_t argument(struct instruction *call, unsigned int argno)
165 {
166 	pseudo_t args[8];
167 	struct ptr_list *arg_list = (struct ptr_list *) call->arguments;
168 
169 	argno--;
170 	if (linearize_ptr_list(arg_list, (void *)args, 8) > argno)
171 		return args[argno];
172 	return NULL;
173 }
174 
175 static void check_memset(struct instruction *insn)
176 {
177 	check_byte_count(insn, argument(insn, 3));
178 }
179 
180 #define check_memcpy check_memset
181 #define check_ctu check_memset
182 #define check_cfu check_memset
183 
184 struct checkfn {
185 	struct ident *id;
186 	void (*check)(struct instruction *insn);
187 };
188 
189 static void check_call_instruction(struct instruction *insn)
190 {
191 	pseudo_t fn = insn->func;
192 	struct ident *ident;
193 	static const struct checkfn check_fn[] = {
194 		{ &memset_ident, check_memset },
195 		{ &memcpy_ident, check_memcpy },
196 		{ &copy_to_user_ident, check_ctu },
197 		{ &copy_from_user_ident, check_cfu },
198 	};
199 	int i;
200 
201 	if (fn->type != PSEUDO_SYM)
202 		return;
203 	ident = fn->sym->ident;
204 	if (!ident)
205 		return;
206 	for (i = 0; i < ARRAY_SIZE(check_fn); i++) {
207 		if (check_fn[i].id != ident)
208 			continue;
209 		check_fn[i].check(insn);
210 		break;
211 	}
212 }
213 
214 static void check_one_instruction(struct instruction *insn)
215 {
216 	switch (insn->opcode) {
217 	case OP_CAST: case OP_SCAST:
218 		if (verbose)
219 			check_cast_instruction(insn);
220 		break;
221 	case OP_RANGE:
222 		check_range_instruction(insn);
223 		break;
224 	case OP_CALL:
225 		check_call_instruction(insn);
226 		break;
227 	default:
228 		break;
229 	}
230 }
231 
232 static void check_bb_instructions(struct basic_block *bb)
233 {
234 	struct instruction *insn;
235 	FOR_EACH_PTR(bb->insns, insn) {
236 		if (!insn->bb)
237 			continue;
238 		check_one_instruction(insn);
239 	} END_FOR_EACH_PTR(insn);
240 }
241 
242 static void check_instructions(struct entrypoint *ep)
243 {
244 	struct basic_block *bb;
245 	FOR_EACH_PTR(ep->bbs, bb) {
246 		check_bb_instructions(bb);
247 	} END_FOR_EACH_PTR(bb);
248 }
249 
250 static void check_context(struct entrypoint *ep)
251 {
252 	struct symbol *sym = ep->name;
253 	struct context *context;
254 	unsigned int in_context = 0, out_context = 0;
255 
256 	if (Wuninitialized && verbose && ep->entry->bb->needs) {
257 		pseudo_t pseudo;
258 		FOR_EACH_PTR(ep->entry->bb->needs, pseudo) {
259 			if (pseudo->type != PSEUDO_ARG)
260 				warning(sym->pos, "%s: possible uninitialized variable (%s)",
261 					show_ident(sym->ident), show_pseudo(pseudo));
262 		} END_FOR_EACH_PTR(pseudo);
263 	}
264 
265 	check_instructions(ep);
266 
267 	FOR_EACH_PTR(sym->ctype.contexts, context) {
268 		in_context += context->in;
269 		out_context += context->out;
270 	} END_FOR_EACH_PTR(context);
271 	check_bb_context(ep, ep->entry->bb, in_context, out_context);
272 }
273 
274 static void check_symbols(struct symbol_list *list)
275 {
276 	struct symbol *sym;
277 
278 	FOR_EACH_PTR(list, sym) {
279 		struct entrypoint *ep;
280 
281 		expand_symbol(sym);
282 		ep = linearize_symbol(sym);
283 		if (ep) {
284 			if (dbg_entry)
285 				show_entry(ep);
286 
287 			check_context(ep);
288 		}
289 	} END_FOR_EACH_PTR(sym);
290 
291 	if (Wsparse_error && die_if_error)
292 		exit(1);
293 }
294 
295 int main(int argc, char **argv)
296 {
297 	struct string_list *filelist = NULL;
298 	char *file;
299 
300 	// Expand, linearize and show it.
301 	check_symbols(sparse_initialize(argc, argv, &filelist));
302 	FOR_EACH_PTR_NOTAG(filelist, file) {
303 		check_symbols(sparse(file));
304 	} END_FOR_EACH_PTR_NOTAG(file);
305 
306 	report_stats();
307 	return 0;
308 }
309