db: print less --info for a few problematic functions
[smatch.git] / check_string_len.c
blob37467a4228d702c4f8554e438825584a6c886b46
1 /*
2 * smatch/check_string_len.c
4 * Copyright (C) 2013 Oracle.
6 * Licensed under the Open Software License version 1.1
8 */
11 * This tries to find buffer overflows in sprintf().
12 * I'll freely admit that the code is sort of crap.
13 * Also if it sees "sprintf("%2d\n", x)" then it assumes x is less than 99.
14 * That might not be true so there maybe buffer overflows which are missed.
18 #include <ctype.h>
19 #include "smatch.h"
21 static int my_id;
23 struct param_info {
24 int buf_or_limit;
25 int string;
28 struct param_info zero_one = {0, 1};
30 static int handle_format(struct expression *call, char **pp, int *arg_nr)
32 struct expression *arg;
33 char *p = *pp;
34 int ret = 1;
35 char buf[256];
36 sval_t max;
38 p++; /* we passed it with *p == '%' */
40 if (*p == '%') {
41 p++;
42 ret = 1;
43 goto out_no_arg;
45 if (*p == 'c') {
46 p++;
47 ret = 1;
48 goto out;
52 if (isdigit(*p) || *p == '.') {
53 unsigned long num;
55 if (*p == '.')
56 p++;
58 num = strtoul(p, &p, 10);
59 ret = num;
61 while (*p == 'l')
62 p++;
63 p++; /* eat the 'd' char */
64 goto out;
67 if (*p == 'l') {
68 p++;
69 if (*p == 'l')
70 p++;
73 if (option_project == PROJ_KERNEL && *p == 'z')
74 p++;
76 if (option_project == PROJ_KERNEL && *p == 'p') {
77 if (*(p + 1) == 'I' || *(p + 1) == 'i') {
78 char *eye;
80 eye = p + 1;
81 p += 2;
82 if (*p == 'h' || *p == 'n' || *p == 'b' || *p == 'l')
83 p++;
84 if (*p == '4') {
85 p++;
86 ret = 15;
87 goto out;
89 if (*p == '6') {
90 p++;
91 if (*p == 'c')
92 p++;
93 if (*eye == 'I')
94 ret = 39;
95 if (*eye == 'i')
96 ret = 32;
97 goto out;
100 if (*(p + 1) == 'M') {
101 p += 2;
102 if (*p == 'R' || *p == 'F')
103 p++;
104 ret = 17;
105 goto out;
107 if (*(p + 1) == 'm') {
108 p += 2;
109 if (*p == 'R')
110 p++;
111 ret = 12;
112 goto out;
116 arg = get_argument_from_call_expr(call->args, *arg_nr);
117 if (!arg)
118 goto out;
120 if (*p == 's') {
121 ret = get_array_size_bytes(arg);
122 if (ret < 0)
123 ret = 1;
124 /* we don't print the NUL here */
125 ret--;
126 p++;
127 goto out;
130 if (*p != 'd' && *p != 'i' && *p != 'x' && *p != 'X' && *p != 'u' && *p != 'p') {
131 ret = 1;
132 p++;
133 goto out;
136 get_absolute_max(arg, &max);
138 if (*p == 'x' || *p == 'X' || *p == 'p') {
139 ret = snprintf(buf, sizeof(buf), "%llx", max.uvalue);
140 } else if (*p == 'u') {
141 ret = snprintf(buf, sizeof(buf), "%llu", max.uvalue);
142 } else if (!expr_unsigned(arg)) {
143 sval_t min;
144 int tmp;
146 ret = snprintf(buf, sizeof(buf), "%lld", max.value);
147 get_absolute_min(arg, &min);
148 tmp = snprintf(buf, sizeof(buf), "%lld", min.value);
149 if (tmp > ret)
150 ret = tmp;
151 } else {
152 ret = snprintf(buf, sizeof(buf), "%lld", max.value);
154 p++;
156 out:
157 (*arg_nr)++;
158 out_no_arg:
159 *pp = p;
160 return ret;
163 static int get_string_size(struct expression *call, int arg)
165 struct expression *expr;
166 char *p;
167 int count;
169 expr = get_argument_from_call_expr(call->args, arg);
170 if (!expr || expr->type != EXPR_STRING)
171 return -1;
173 arg++;
174 count = 0;
175 p = expr->string->data;
176 while (*p) {
178 if (*p == '%') {
179 count += handle_format(call, &p, &arg);
180 } else if (*p == '\\') {
181 p++;
182 }else {
183 p++;
184 count++;
188 count++; /* count the NUL terminator */
189 return count;
192 static void match_not_limited(const char *fn, struct expression *call, void *info)
194 struct param_info *params = info;
195 struct expression *arg;
196 int buf_size, size;
197 int user = 0;
198 int i;
200 arg = get_argument_from_call_expr(call->args, params->buf_or_limit);
201 buf_size = get_array_size_bytes(arg);
202 if (buf_size <= 0)
203 return;
205 size = get_string_size(call, params->string);
206 if (size <= 0)
207 return;
208 if (size <= buf_size)
209 return;
211 i = 0;
212 FOR_EACH_PTR(call->args, arg) {
213 if (i++ <= params->string)
214 continue;
215 if (is_user_data(arg))
216 user = 1;
217 } END_FOR_EACH_PTR(arg);
219 sm_msg("error: format string overflow. buf_size: %d length: %d%s",
220 buf_size, size, user ? " [user data]": "");
223 void check_string_len(int id)
225 my_id = id;
226 add_function_hook("sprintf", &match_not_limited, &zero_one);