|
125 | 125 | * are set to NOT_INIT to indicate that they are no longer readable.
|
126 | 126 | */
|
127 | 127 |
|
| 128 | +/* single container for all structs |
| 129 | + * one verifier_env per bpf_check() call |
| 130 | + */ |
| 131 | +struct verifier_env { |
| 132 | +}; |
| 133 | + |
| 134 | +/* verbose verifier prints what it's seeing |
| 135 | + * bpf_check() is called under lock, so no race to access these global vars |
| 136 | + */ |
| 137 | +static u32 log_level, log_size, log_len; |
| 138 | +static char *log_buf; |
| 139 | + |
| 140 | +static DEFINE_MUTEX(bpf_verifier_lock); |
| 141 | + |
| 142 | +/* log_level controls verbosity level of eBPF verifier. |
| 143 | + * verbose() is used to dump the verification trace to the log, so the user |
| 144 | + * can figure out what's wrong with the program |
| 145 | + */ |
| 146 | +static void verbose(const char *fmt, ...) |
| 147 | +{ |
| 148 | + va_list args; |
| 149 | + |
| 150 | + if (log_level == 0 || log_len >= log_size - 1) |
| 151 | + return; |
| 152 | + |
| 153 | + va_start(args, fmt); |
| 154 | + log_len += vscnprintf(log_buf + log_len, log_size - log_len, fmt, args); |
| 155 | + va_end(args); |
| 156 | +} |
| 157 | + |
| 158 | +static const char *const bpf_class_string[] = { |
| 159 | + [BPF_LD] = "ld", |
| 160 | + [BPF_LDX] = "ldx", |
| 161 | + [BPF_ST] = "st", |
| 162 | + [BPF_STX] = "stx", |
| 163 | + [BPF_ALU] = "alu", |
| 164 | + [BPF_JMP] = "jmp", |
| 165 | + [BPF_RET] = "BUG", |
| 166 | + [BPF_ALU64] = "alu64", |
| 167 | +}; |
| 168 | + |
| 169 | +static const char *const bpf_alu_string[] = { |
| 170 | + [BPF_ADD >> 4] = "+=", |
| 171 | + [BPF_SUB >> 4] = "-=", |
| 172 | + [BPF_MUL >> 4] = "*=", |
| 173 | + [BPF_DIV >> 4] = "/=", |
| 174 | + [BPF_OR >> 4] = "|=", |
| 175 | + [BPF_AND >> 4] = "&=", |
| 176 | + [BPF_LSH >> 4] = "<<=", |
| 177 | + [BPF_RSH >> 4] = ">>=", |
| 178 | + [BPF_NEG >> 4] = "neg", |
| 179 | + [BPF_MOD >> 4] = "%=", |
| 180 | + [BPF_XOR >> 4] = "^=", |
| 181 | + [BPF_MOV >> 4] = "=", |
| 182 | + [BPF_ARSH >> 4] = "s>>=", |
| 183 | + [BPF_END >> 4] = "endian", |
| 184 | +}; |
| 185 | + |
| 186 | +static const char *const bpf_ldst_string[] = { |
| 187 | + [BPF_W >> 3] = "u32", |
| 188 | + [BPF_H >> 3] = "u16", |
| 189 | + [BPF_B >> 3] = "u8", |
| 190 | + [BPF_DW >> 3] = "u64", |
| 191 | +}; |
| 192 | + |
| 193 | +static const char *const bpf_jmp_string[] = { |
| 194 | + [BPF_JA >> 4] = "jmp", |
| 195 | + [BPF_JEQ >> 4] = "==", |
| 196 | + [BPF_JGT >> 4] = ">", |
| 197 | + [BPF_JGE >> 4] = ">=", |
| 198 | + [BPF_JSET >> 4] = "&", |
| 199 | + [BPF_JNE >> 4] = "!=", |
| 200 | + [BPF_JSGT >> 4] = "s>", |
| 201 | + [BPF_JSGE >> 4] = "s>=", |
| 202 | + [BPF_CALL >> 4] = "call", |
| 203 | + [BPF_EXIT >> 4] = "exit", |
| 204 | +}; |
| 205 | + |
| 206 | +static void print_bpf_insn(struct bpf_insn *insn) |
| 207 | +{ |
| 208 | + u8 class = BPF_CLASS(insn->code); |
| 209 | + |
| 210 | + if (class == BPF_ALU || class == BPF_ALU64) { |
| 211 | + if (BPF_SRC(insn->code) == BPF_X) |
| 212 | + verbose("(%02x) %sr%d %s %sr%d\n", |
| 213 | + insn->code, class == BPF_ALU ? "(u32) " : "", |
| 214 | + insn->dst_reg, |
| 215 | + bpf_alu_string[BPF_OP(insn->code) >> 4], |
| 216 | + class == BPF_ALU ? "(u32) " : "", |
| 217 | + insn->src_reg); |
| 218 | + else |
| 219 | + verbose("(%02x) %sr%d %s %s%d\n", |
| 220 | + insn->code, class == BPF_ALU ? "(u32) " : "", |
| 221 | + insn->dst_reg, |
| 222 | + bpf_alu_string[BPF_OP(insn->code) >> 4], |
| 223 | + class == BPF_ALU ? "(u32) " : "", |
| 224 | + insn->imm); |
| 225 | + } else if (class == BPF_STX) { |
| 226 | + if (BPF_MODE(insn->code) == BPF_MEM) |
| 227 | + verbose("(%02x) *(%s *)(r%d %+d) = r%d\n", |
| 228 | + insn->code, |
| 229 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 230 | + insn->dst_reg, |
| 231 | + insn->off, insn->src_reg); |
| 232 | + else if (BPF_MODE(insn->code) == BPF_XADD) |
| 233 | + verbose("(%02x) lock *(%s *)(r%d %+d) += r%d\n", |
| 234 | + insn->code, |
| 235 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 236 | + insn->dst_reg, insn->off, |
| 237 | + insn->src_reg); |
| 238 | + else |
| 239 | + verbose("BUG_%02x\n", insn->code); |
| 240 | + } else if (class == BPF_ST) { |
| 241 | + if (BPF_MODE(insn->code) != BPF_MEM) { |
| 242 | + verbose("BUG_st_%02x\n", insn->code); |
| 243 | + return; |
| 244 | + } |
| 245 | + verbose("(%02x) *(%s *)(r%d %+d) = %d\n", |
| 246 | + insn->code, |
| 247 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 248 | + insn->dst_reg, |
| 249 | + insn->off, insn->imm); |
| 250 | + } else if (class == BPF_LDX) { |
| 251 | + if (BPF_MODE(insn->code) != BPF_MEM) { |
| 252 | + verbose("BUG_ldx_%02x\n", insn->code); |
| 253 | + return; |
| 254 | + } |
| 255 | + verbose("(%02x) r%d = *(%s *)(r%d %+d)\n", |
| 256 | + insn->code, insn->dst_reg, |
| 257 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 258 | + insn->src_reg, insn->off); |
| 259 | + } else if (class == BPF_LD) { |
| 260 | + if (BPF_MODE(insn->code) == BPF_ABS) { |
| 261 | + verbose("(%02x) r0 = *(%s *)skb[%d]\n", |
| 262 | + insn->code, |
| 263 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 264 | + insn->imm); |
| 265 | + } else if (BPF_MODE(insn->code) == BPF_IND) { |
| 266 | + verbose("(%02x) r0 = *(%s *)skb[r%d + %d]\n", |
| 267 | + insn->code, |
| 268 | + bpf_ldst_string[BPF_SIZE(insn->code) >> 3], |
| 269 | + insn->src_reg, insn->imm); |
| 270 | + } else if (BPF_MODE(insn->code) == BPF_IMM) { |
| 271 | + verbose("(%02x) r%d = 0x%x\n", |
| 272 | + insn->code, insn->dst_reg, insn->imm); |
| 273 | + } else { |
| 274 | + verbose("BUG_ld_%02x\n", insn->code); |
| 275 | + return; |
| 276 | + } |
| 277 | + } else if (class == BPF_JMP) { |
| 278 | + u8 opcode = BPF_OP(insn->code); |
| 279 | + |
| 280 | + if (opcode == BPF_CALL) { |
| 281 | + verbose("(%02x) call %d\n", insn->code, insn->imm); |
| 282 | + } else if (insn->code == (BPF_JMP | BPF_JA)) { |
| 283 | + verbose("(%02x) goto pc%+d\n", |
| 284 | + insn->code, insn->off); |
| 285 | + } else if (insn->code == (BPF_JMP | BPF_EXIT)) { |
| 286 | + verbose("(%02x) exit\n", insn->code); |
| 287 | + } else if (BPF_SRC(insn->code) == BPF_X) { |
| 288 | + verbose("(%02x) if r%d %s r%d goto pc%+d\n", |
| 289 | + insn->code, insn->dst_reg, |
| 290 | + bpf_jmp_string[BPF_OP(insn->code) >> 4], |
| 291 | + insn->src_reg, insn->off); |
| 292 | + } else { |
| 293 | + verbose("(%02x) if r%d %s 0x%x goto pc%+d\n", |
| 294 | + insn->code, insn->dst_reg, |
| 295 | + bpf_jmp_string[BPF_OP(insn->code) >> 4], |
| 296 | + insn->imm, insn->off); |
| 297 | + } |
| 298 | + } else { |
| 299 | + verbose("(%02x) %s\n", insn->code, bpf_class_string[class]); |
| 300 | + } |
| 301 | +} |
| 302 | + |
128 | 303 | int bpf_check(struct bpf_prog *prog, union bpf_attr *attr)
|
129 | 304 | {
|
| 305 | + char __user *log_ubuf = NULL; |
| 306 | + struct verifier_env *env; |
130 | 307 | int ret = -EINVAL;
|
131 | 308 |
|
| 309 | + if (prog->len <= 0 || prog->len > BPF_MAXINSNS) |
| 310 | + return -E2BIG; |
| 311 | + |
| 312 | + /* 'struct verifier_env' can be global, but since it's not small, |
| 313 | + * allocate/free it every time bpf_check() is called |
| 314 | + */ |
| 315 | + env = kzalloc(sizeof(struct verifier_env), GFP_KERNEL); |
| 316 | + if (!env) |
| 317 | + return -ENOMEM; |
| 318 | + |
| 319 | + /* grab the mutex to protect few globals used by verifier */ |
| 320 | + mutex_lock(&bpf_verifier_lock); |
| 321 | + |
| 322 | + if (attr->log_level || attr->log_buf || attr->log_size) { |
| 323 | + /* user requested verbose verifier output |
| 324 | + * and supplied buffer to store the verification trace |
| 325 | + */ |
| 326 | + log_level = attr->log_level; |
| 327 | + log_ubuf = (char __user *) (unsigned long) attr->log_buf; |
| 328 | + log_size = attr->log_size; |
| 329 | + log_len = 0; |
| 330 | + |
| 331 | + ret = -EINVAL; |
| 332 | + /* log_* values have to be sane */ |
| 333 | + if (log_size < 128 || log_size > UINT_MAX >> 8 || |
| 334 | + log_level == 0 || log_ubuf == NULL) |
| 335 | + goto free_env; |
| 336 | + |
| 337 | + ret = -ENOMEM; |
| 338 | + log_buf = vmalloc(log_size); |
| 339 | + if (!log_buf) |
| 340 | + goto free_env; |
| 341 | + } else { |
| 342 | + log_level = 0; |
| 343 | + } |
| 344 | + |
| 345 | + /* ret = do_check(env); */ |
| 346 | + |
| 347 | + if (log_level && log_len >= log_size - 1) { |
| 348 | + BUG_ON(log_len >= log_size); |
| 349 | + /* verifier log exceeded user supplied buffer */ |
| 350 | + ret = -ENOSPC; |
| 351 | + /* fall through to return what was recorded */ |
| 352 | + } |
| 353 | + |
| 354 | + /* copy verifier log back to user space including trailing zero */ |
| 355 | + if (log_level && copy_to_user(log_ubuf, log_buf, log_len + 1) != 0) { |
| 356 | + ret = -EFAULT; |
| 357 | + goto free_log_buf; |
| 358 | + } |
| 359 | + |
| 360 | + |
| 361 | +free_log_buf: |
| 362 | + if (log_level) |
| 363 | + vfree(log_buf); |
| 364 | +free_env: |
| 365 | + kfree(env); |
| 366 | + mutex_unlock(&bpf_verifier_lock); |
132 | 367 | return ret;
|
133 | 368 | }
|
0 commit comments