diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2011-10-07 09:31:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-10-10 03:05:51 -0400 |
commit | 53a019a951fae849471e4a620948c5f6886bd1a4 (patch) | |
tree | 3c37f0297b628e263f74f146352f39cd5401134a /arch | |
parent | 65112dccf8a113737684366349d7f9ec373ddc47 (diff) |
x86: Fix insn decoder for longer instruction
Fix x86 insn decoder for hardening against invalid length
instructions. This adds length checkings for each byte-read
site and if it exceeds MAX_INSN_SIZE, returns immediately.
This can happen when decoding user-space binary.
Caller can check whether it happened by checking insn.*.got
member is set or not.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: acme@redhat.com
Cc: ming.m.lin@intel.com
Cc: robert.richter@amd.com
Cc: ravitillo@lbl.gov
Cc: yrl.pp-manager.tt@hitachi.com
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20111007133155.10933.58577.stgit@localhost.localdomain
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/lib/insn.c | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/arch/x86/lib/insn.c b/arch/x86/lib/insn.c index 9f33b984d0ef..374562ed6704 100644 --- a/arch/x86/lib/insn.c +++ b/arch/x86/lib/insn.c | |||
@@ -22,14 +22,23 @@ | |||
22 | #include <asm/inat.h> | 22 | #include <asm/inat.h> |
23 | #include <asm/insn.h> | 23 | #include <asm/insn.h> |
24 | 24 | ||
25 | #define get_next(t, insn) \ | 25 | /* Verify next sizeof(t) bytes can be on the same instruction */ |
26 | ({t r; r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) | 26 | #define validate_next(t, insn, n) \ |
27 | ((insn)->next_byte + sizeof(t) + n - (insn)->kaddr <= MAX_INSN_SIZE) | ||
28 | |||
29 | #define __get_next(t, insn) \ | ||
30 | ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) | ||
31 | |||
32 | #define __peek_nbyte_next(t, insn, n) \ | ||
33 | ({ t r = *(t*)((insn)->next_byte + n); r; }) | ||
27 | 34 | ||
28 | #define peek_next(t, insn) \ | 35 | #define get_next(t, insn) \ |
29 | ({t r; r = *(t*)insn->next_byte; r; }) | 36 | ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) |
30 | 37 | ||
31 | #define peek_nbyte_next(t, insn, n) \ | 38 | #define peek_nbyte_next(t, insn, n) \ |
32 | ({t r; r = *(t*)((insn)->next_byte + n); r; }) | 39 | ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); }) |
40 | |||
41 | #define peek_next(t, insn) peek_nbyte_next(t, insn, 0) | ||
33 | 42 | ||
34 | /** | 43 | /** |
35 | * insn_init() - initialize struct insn | 44 | * insn_init() - initialize struct insn |
@@ -158,6 +167,8 @@ vex_end: | |||
158 | insn->vex_prefix.got = 1; | 167 | insn->vex_prefix.got = 1; |
159 | 168 | ||
160 | prefixes->got = 1; | 169 | prefixes->got = 1; |
170 | |||
171 | err_out: | ||
161 | return; | 172 | return; |
162 | } | 173 | } |
163 | 174 | ||
@@ -208,6 +219,9 @@ void insn_get_opcode(struct insn *insn) | |||
208 | insn->attr = 0; /* This instruction is bad */ | 219 | insn->attr = 0; /* This instruction is bad */ |
209 | end: | 220 | end: |
210 | opcode->got = 1; | 221 | opcode->got = 1; |
222 | |||
223 | err_out: | ||
224 | return; | ||
211 | } | 225 | } |
212 | 226 | ||
213 | /** | 227 | /** |
@@ -241,6 +255,9 @@ void insn_get_modrm(struct insn *insn) | |||
241 | if (insn->x86_64 && inat_is_force64(insn->attr)) | 255 | if (insn->x86_64 && inat_is_force64(insn->attr)) |
242 | insn->opnd_bytes = 8; | 256 | insn->opnd_bytes = 8; |
243 | modrm->got = 1; | 257 | modrm->got = 1; |
258 | |||
259 | err_out: | ||
260 | return; | ||
244 | } | 261 | } |
245 | 262 | ||
246 | 263 | ||
@@ -290,6 +307,9 @@ void insn_get_sib(struct insn *insn) | |||
290 | } | 307 | } |
291 | } | 308 | } |
292 | insn->sib.got = 1; | 309 | insn->sib.got = 1; |
310 | |||
311 | err_out: | ||
312 | return; | ||
293 | } | 313 | } |
294 | 314 | ||
295 | 315 | ||
@@ -351,6 +371,9 @@ void insn_get_displacement(struct insn *insn) | |||
351 | } | 371 | } |
352 | out: | 372 | out: |
353 | insn->displacement.got = 1; | 373 | insn->displacement.got = 1; |
374 | |||
375 | err_out: | ||
376 | return; | ||
354 | } | 377 | } |
355 | 378 | ||
356 | /* Decode moffset16/32/64 */ | 379 | /* Decode moffset16/32/64 */ |
@@ -373,6 +396,9 @@ static void __get_moffset(struct insn *insn) | |||
373 | break; | 396 | break; |
374 | } | 397 | } |
375 | insn->moffset1.got = insn->moffset2.got = 1; | 398 | insn->moffset1.got = insn->moffset2.got = 1; |
399 | |||
400 | err_out: | ||
401 | return; | ||
376 | } | 402 | } |
377 | 403 | ||
378 | /* Decode imm v32(Iz) */ | 404 | /* Decode imm v32(Iz) */ |
@@ -389,6 +415,9 @@ static void __get_immv32(struct insn *insn) | |||
389 | insn->immediate.nbytes = 4; | 415 | insn->immediate.nbytes = 4; |
390 | break; | 416 | break; |
391 | } | 417 | } |
418 | |||
419 | err_out: | ||
420 | return; | ||
392 | } | 421 | } |
393 | 422 | ||
394 | /* Decode imm v64(Iv/Ov) */ | 423 | /* Decode imm v64(Iv/Ov) */ |
@@ -411,6 +440,9 @@ static void __get_immv(struct insn *insn) | |||
411 | break; | 440 | break; |
412 | } | 441 | } |
413 | insn->immediate1.got = insn->immediate2.got = 1; | 442 | insn->immediate1.got = insn->immediate2.got = 1; |
443 | |||
444 | err_out: | ||
445 | return; | ||
414 | } | 446 | } |
415 | 447 | ||
416 | /* Decode ptr16:16/32(Ap) */ | 448 | /* Decode ptr16:16/32(Ap) */ |
@@ -432,6 +464,9 @@ static void __get_immptr(struct insn *insn) | |||
432 | insn->immediate2.value = get_next(unsigned short, insn); | 464 | insn->immediate2.value = get_next(unsigned short, insn); |
433 | insn->immediate2.nbytes = 2; | 465 | insn->immediate2.nbytes = 2; |
434 | insn->immediate1.got = insn->immediate2.got = 1; | 466 | insn->immediate1.got = insn->immediate2.got = 1; |
467 | |||
468 | err_out: | ||
469 | return; | ||
435 | } | 470 | } |
436 | 471 | ||
437 | /** | 472 | /** |
@@ -496,6 +531,9 @@ void insn_get_immediate(struct insn *insn) | |||
496 | } | 531 | } |
497 | done: | 532 | done: |
498 | insn->immediate.got = 1; | 533 | insn->immediate.got = 1; |
534 | |||
535 | err_out: | ||
536 | return; | ||
499 | } | 537 | } |
500 | 538 | ||
501 | /** | 539 | /** |