1 /*
2  * Copyright (C) 2013 Oracle.
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  * This tries to find buffer overflows in sprintf().
20  * I'll freely admit that the code is sort of crap.
21  * Also if it sees "sprintf("%2d\n", x)" then it assumes x is less than 99.
22  * That might not be true so there maybe buffer overflows which are missed.
23  *
24  */
25 
26 #include <ctype.h>
27 #include "smatch.h"
28 
29 static int my_id;
30 
31 struct param_info {
32 	int buf_or_limit;
33 	int string;
34 };
35 
36 struct param_info zero_one = {0, 1};
37 
handle_format(struct expression * call,char ** pp,int * arg_nr,bool use_max)38 static int handle_format(struct expression *call, char **pp, int *arg_nr, bool use_max)
39 {
40 	struct expression *arg;
41 	char *p = *pp;
42 	int ret = 1;
43 	char buf[256];
44 	sval_t sval;
45 
46 	p++;  /* we passed it with *p == '%' */
47 
48 	if (*p == '%') {
49 		p++;
50 		ret = 1;
51 		goto out_no_arg;
52 	}
53 	if (*p == 'c') {
54 		p++;
55 		ret = 1;
56 		goto out;
57 	}
58 
59 
60 	if (isdigit(*p) || *p == '.') {
61 		unsigned long num;
62 
63 		if (*p == '.')
64 			p++;
65 
66 		num = strtoul(p, &p, 10);
67 		ret = num;
68 
69 		while (*p == 'l')
70 			p++;
71 		p++; /* eat the 'd' char */
72 		goto out;
73 	}
74 
75 	if (*p == 'l') {
76 		p++;
77 		if (*p == 'l')
78 			p++;
79 	}
80 
81 	if (option_project == PROJ_KERNEL && *p == 'z')
82 		p++;
83 
84 	if (option_project == PROJ_KERNEL && *p == 'p') {
85 		if (*(p + 1) == 'I' || *(p + 1) == 'i') {
86 			char *eye;
87 
88 			eye = p + 1;
89 			p += 2;
90 			if (*p == 'h' || *p == 'n' || *p == 'b' || *p == 'l')
91 				p++;
92 			if (*p == '4') {
93 				p++;
94 				ret = 15;
95 				goto out;
96 			}
97 			if (*p == '6') {
98 				p++;
99 				if (*p == 'c')
100 					p++;
101 				if (*eye == 'I')
102 					ret = 39;
103 				if (*eye == 'i')
104 					ret = 32;
105 				goto out;
106 			}
107 		}
108 		if (*(p + 1) == 'M') {
109 			p += 2;
110 			if (*p == 'R' || *p == 'F')
111 				p++;
112 			ret = 17;
113 			goto out;
114 		}
115 		if (*(p + 1) == 'm') {
116 			p += 2;
117 			if (*p == 'R')
118 				p++;
119 			ret = 12;
120 			goto out;
121 		}
122 	}
123 
124 	arg = get_argument_from_call_expr(call->args, *arg_nr);
125 	if (!arg)
126 		goto out;
127 
128 	if (*p == 's') {
129 		ret = get_array_size_bytes(arg);
130 		if (ret < 0)
131 			ret = 1;
132 		/* we don't print the NUL here */
133 		ret--;
134 		p++;
135 		goto out;
136 	}
137 
138 	if (*p != 'd' && *p != 'i' && *p != 'x' && *p != 'X' && *p != 'u' && *p != 'p') {
139 		ret = 1;
140 		p++;
141 		goto out;
142 	}
143 
144 	if (use_max) {
145 		get_absolute_max(arg, &sval);
146 	} else {
147 		get_absolute_min(arg, &sval);
148 		if (sval_is_negative(sval))
149 			sval.value = 0;
150 	}
151 
152 
153 	if (*p == 'x' || *p == 'X' || *p == 'p') {
154 		ret = snprintf(buf, sizeof(buf), "%llx", sval.uvalue);
155 	} else if (*p == 'u') {
156 		ret = snprintf(buf, sizeof(buf), "%llu", sval.uvalue);
157 	} else if (!expr_unsigned(arg)) {
158 		sval_t min;
159 		int tmp;
160 
161 		ret = snprintf(buf, sizeof(buf), "%lld", sval.value);
162 		get_absolute_min(arg, &min);
163 		tmp = snprintf(buf, sizeof(buf), "%lld", min.value);
164 		if (tmp > ret)
165 			ret = tmp;
166 	} else {
167 		ret = snprintf(buf, sizeof(buf), "%lld", sval.value);
168 	}
169 	p++;
170 
171 out:
172 	(*arg_nr)++;
173 out_no_arg:
174 	*pp = p;
175 	return ret;
176 }
177 
get_formatted_string_size_helper(struct expression * call,int arg,bool use_max)178 int get_formatted_string_size_helper(struct expression *call, int arg, bool use_max)
179 {
180 	struct expression *expr;
181 	char *p;
182 	int count;
183 
184 	expr = get_argument_from_call_expr(call->args, arg);
185 	if (!expr || expr->type != EXPR_STRING)
186 		return -1;
187 
188 	arg++;
189 	count = 0;
190 	p = expr->string->data;
191 	while (*p) {
192 
193 		if (*p == '%') {
194 			count += handle_format(call, &p, &arg, use_max);
195 		} else if (*p == '\\') {
196 			p++;
197 		}else {
198 			p++;
199 			count++;
200 		}
201 	}
202 
203 	return count;
204 }
205 
get_formatted_string_size(struct expression * call,int arg)206 int get_formatted_string_size(struct expression *call, int arg)
207 {
208 	return get_formatted_string_size_helper(call, arg, true);
209 }
210 
get_formatted_string_min_size(struct expression * call,int arg)211 int get_formatted_string_min_size(struct expression *call, int arg)
212 {
213 	return get_formatted_string_size_helper(call, arg, false);
214 }
215 
match_not_limited(const char * fn,struct expression * call,void * info)216 static void match_not_limited(const char *fn, struct expression *call, void *info)
217 {
218 	struct param_info *params = info;
219 	struct range_list *rl;
220 	struct expression *dest;
221 	struct expression *arg;
222 	int buf_size, size;
223 	int user = 0;
224 	int i;
225 	int offset = 0;
226 
227 	dest = get_argument_from_call_expr(call->args, params->buf_or_limit);
228 	dest = strip_expr(dest);
229 	if (dest->type == EXPR_BINOP && dest->op == '+') {
230 		sval_t max;
231 
232 		if (get_hard_max(dest->right, &max))
233 			offset = max.value;
234 		dest = dest->left;
235 	}
236 
237 
238 	buf_size = get_array_size_bytes(dest);
239 	if (buf_size <= 0)
240 		return;
241 
242 	size = get_formatted_string_size(call, params->string);
243 	if (size < 0)
244 		return;
245 	if (size < offset)
246 		size -= offset;
247 	size++; /* add the NULL terminator */
248 	if (size <= buf_size)
249 		return;
250 
251 	i = 0;
252 	FOR_EACH_PTR(call->args, arg) {
253 		if (i++ <= params->string)
254 			continue;
255 		if (get_user_rl(arg, &rl))
256 			user = 1;
257 	} END_FOR_EACH_PTR(arg);
258 
259 	sm_error("format string overflow. buf_size: %d length: %d%s",
260 	       buf_size, size, user ? " [user data]": "");
261 }
262 
check_string_len(int id)263 void check_string_len(int id)
264 {
265 	my_id = id;
266 	add_function_hook("sprintf", &match_not_limited, &zero_one);
267 }
268 
269