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) 39 { 40 struct expression *arg; 41 char *p = *pp; 42 int ret = 1; 43 char buf[256]; 44 sval_t max; 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 get_absolute_max(arg, &max); 145 146 if (*p == 'x' || *p == 'X' || *p == 'p') { 147 ret = snprintf(buf, sizeof(buf), "%llx", max.uvalue); 148 } else if (*p == 'u') { 149 ret = snprintf(buf, sizeof(buf), "%llu", max.uvalue); 150 } else if (!expr_unsigned(arg)) { 151 sval_t min; 152 int tmp; 153 154 ret = snprintf(buf, sizeof(buf), "%lld", max.value); 155 get_absolute_min(arg, &min); 156 tmp = snprintf(buf, sizeof(buf), "%lld", min.value); 157 if (tmp > ret) 158 ret = tmp; 159 } else { 160 ret = snprintf(buf, sizeof(buf), "%lld", max.value); 161 } 162 p++; 163 164 out: 165 (*arg_nr)++; 166 out_no_arg: 167 *pp = p; 168 return ret; 169 } 170 171 int get_formatted_string_size(struct expression *call, int arg) 172 { 173 struct expression *expr; 174 char *p; 175 int count; 176 177 expr = get_argument_from_call_expr(call->args, arg); 178 if (!expr || expr->type != EXPR_STRING) 179 return -1; 180 181 arg++; 182 count = 0; 183 p = expr->string->data; 184 while (*p) { 185 186 if (*p == '%') { 187 count += handle_format(call, &p, &arg); 188 } else if (*p == '\\') { 189 p++; 190 }else { 191 p++; 192 count++; 193 } 194 } 195 196 count++; /* count the NUL terminator */ 197 return count; 198 } 199 200 static void match_not_limited(const char *fn, struct expression *call, void *info) 201 { 202 struct param_info *params = info; 203 struct range_list *rl; 204 struct expression *dest; 205 struct expression *arg; 206 int buf_size, size; 207 int user = 0; 208 int i; 209 int offset = 0; 210 211 dest = get_argument_from_call_expr(call->args, params->buf_or_limit); 212 dest = strip_expr(dest); 213 if (dest->type == EXPR_BINOP && dest->op == '+') { 214 sval_t max; 215 216 if (get_hard_max(dest->right, &max)) 217 offset = max.value; 218 dest = dest->left; 219 } 220 221 222 buf_size = get_array_size_bytes(dest); 223 if (buf_size <= 0) 224 return; 225 226 size = get_formatted_string_size(call, params->string); 227 if (size <= 0) 228 return; 229 if (size < offset) 230 size -= offset; 231 if (size <= buf_size) 232 return; 233 234 i = 0; 235 FOR_EACH_PTR(call->args, arg) { 236 if (i++ <= params->string) 237 continue; 238 if (get_user_rl(arg, &rl)) 239 user = 1; 240 } END_FOR_EACH_PTR(arg); 241 242 sm_error("format string overflow. buf_size: %d length: %d%s", 243 buf_size, size, user ? " [user data]": ""); 244 } 245 246 void check_string_len(int id) 247 { 248 my_id = id; 249 add_function_hook("sprintf", &match_not_limited, &zero_one); 250 } 251 252