unwind: separate path states out into a different check id
[smatch.git] / check_string_len.c
blobf8c102ae01b8dd6a57accbd5d1f70932c33da83b
1 /*
2 * Copyright (C) 2013 Oracle.
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.
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.
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
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.
26 #include <ctype.h>
27 #include "smatch.h"
29 static int my_id;
31 struct param_info {
32 int buf_or_limit;
33 int string;
36 struct param_info zero_one = {0, 1};
38 static int handle_format(struct expression *call, char **pp, int *arg_nr, bool use_max)
40 struct expression *arg;
41 char *p = *pp;
42 int ret = 1;
43 char buf[256];
44 sval_t sval;
46 p++; /* we passed it with *p == '%' */
48 if (*p == '%') {
49 p++;
50 ret = 1;
51 goto out_no_arg;
53 if (*p == 'c') {
54 p++;
55 ret = 1;
56 goto out;
60 if (isdigit(*p) || *p == '.') {
61 unsigned long num;
63 if (*p == '.')
64 p++;
66 num = strtoul(p, &p, 10);
67 ret = num;
69 while (*p == 'l')
70 p++;
71 p++; /* eat the 'd' char */
72 goto out;
75 if (*p == 'l') {
76 p++;
77 if (*p == 'l')
78 p++;
81 if (option_project == PROJ_KERNEL && *p == 'z')
82 p++;
84 if (option_project == PROJ_KERNEL && *p == 'p') {
85 if (*(p + 1) == 'I' || *(p + 1) == 'i') {
86 char *eye;
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;
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;
108 if (*(p + 1) == 'M') {
109 p += 2;
110 if (*p == 'R' || *p == 'F')
111 p++;
112 ret = 17;
113 goto out;
115 if (*(p + 1) == 'm') {
116 p += 2;
117 if (*p == 'R')
118 p++;
119 ret = 12;
120 goto out;
124 arg = get_argument_from_call_expr(call->args, *arg_nr);
125 if (!arg)
126 goto out;
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;
138 if (*p != 'd' && *p != 'i' && *p != 'x' && *p != 'X' && *p != 'u' && *p != 'p') {
139 ret = 1;
140 p++;
141 goto out;
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;
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;
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);
169 p++;
171 out:
172 (*arg_nr)++;
173 out_no_arg:
174 *pp = p;
175 return ret;
178 int get_formatted_string_size_helper(struct expression *call, int arg, bool use_max)
180 struct expression *expr;
181 char *p;
182 int count;
184 expr = get_argument_from_call_expr(call->args, arg);
185 if (!expr || expr->type != EXPR_STRING)
186 return -1;
188 arg++;
189 count = 0;
190 p = expr->string->data;
191 while (*p) {
193 if (*p == '%') {
194 count += handle_format(call, &p, &arg, use_max);
195 } else if (*p == '\\') {
196 p++;
197 }else {
198 p++;
199 count++;
203 return count;
206 int get_formatted_string_size(struct expression *call, int arg)
208 return get_formatted_string_size_helper(call, arg, true);
211 int get_formatted_string_min_size(struct expression *call, int arg)
213 return get_formatted_string_size_helper(call, arg, false);
216 static void match_not_limited(const char *fn, struct expression *call, void *info)
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;
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;
232 if (get_hard_max(dest->right, &max))
233 offset = max.value;
234 dest = dest->left;
238 buf_size = get_array_size_bytes(dest);
239 if (buf_size <= 0)
240 return;
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;
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);
259 sm_error("format string overflow. buf_size: %d length: %d%s",
260 buf_size, size, user ? " [user data]": "");
263 void check_string_len(int id)
265 my_id = id;
266 add_function_hook("sprintf", &match_not_limited, &zero_one);