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.
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
;
46 p
++; /* we passed it with *p == '%' */
60 if (isdigit(*p
) || *p
== '.') {
66 num
= strtoul(p
, &p
, 10);
71 p
++; /* eat the 'd' char */
81 if (option_project
== PROJ_KERNEL
&& *p
== 'z')
84 if (option_project
== PROJ_KERNEL
&& *p
== 'p') {
85 if (*(p
+ 1) == 'I' || *(p
+ 1) == 'i') {
90 if (*p
== 'h' || *p
== 'n' || *p
== 'b' || *p
== 'l')
108 if (*(p
+ 1) == 'M') {
110 if (*p
== 'R' || *p
== 'F')
115 if (*(p
+ 1) == 'm') {
124 arg
= get_argument_from_call_expr(call
->args
, *arg_nr
);
129 ret
= get_array_size_bytes(arg
);
132 /* we don't print the NUL here */
138 if (*p
!= 'd' && *p
!= 'i' && *p
!= 'x' && *p
!= 'X' && *p
!= 'u' && *p
!= 'p') {
145 get_absolute_max(arg
, &sval
);
147 get_absolute_min(arg
, &sval
);
148 if (sval_is_negative(sval
))
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
)) {
161 ret
= snprintf(buf
, sizeof(buf
), "%lld", sval
.value
);
162 get_absolute_min(arg
, &min
);
163 tmp
= snprintf(buf
, sizeof(buf
), "%lld", min
.value
);
167 ret
= snprintf(buf
, sizeof(buf
), "%lld", sval
.value
);
178 int get_formatted_string_size_helper(struct expression
*call
, int arg
, bool use_max
)
180 struct expression
*expr
;
184 expr
= get_argument_from_call_expr(call
->args
, arg
);
185 if (!expr
|| expr
->type
!= EXPR_STRING
)
190 p
= expr
->string
->data
;
194 count
+= handle_format(call
, &p
, &arg
, use_max
);
195 } else if (*p
== '\\') {
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
;
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
== '+') {
232 if (get_hard_max(dest
->right
, &max
))
238 buf_size
= get_array_size_bytes(dest
);
242 size
= get_formatted_string_size(call
, params
->string
);
247 size
++; /* add the NULL terminator */
248 if (size
<= buf_size
)
252 FOR_EACH_PTR(call
->args
, arg
) {
253 if (i
++ <= params
->string
)
255 if (get_user_rl(arg
, &rl
))
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
)
266 add_function_hook("sprintf", &match_not_limited
, &zero_one
);