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 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 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 206 int get_formatted_string_size(struct expression *call, int arg) 207 { 208 return get_formatted_string_size_helper(call, arg, true); 209 } 210 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 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 263 void check_string_len(int id) 264 { 265 my_id = id; 266 add_function_hook("sprintf", &match_not_limited, &zero_one); 267 } 268