Teach objtool to validate the straight-line-speculation constraints: - speculation trap after indirect calls - speculation trap after RET Notable: when an instruction is annotated RETPOLINE_SAFE, indicating speculation isn't a problem, also don't care about sls for that instruction.
Unified · Split
| @@ -531,6 +531,11 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec · 530 unmodified lines | |||
| 531 | } | 531 | } |
| 532 | break; | 532 | break; |
| 533 | 533 | ||
| 534 | case 0xcc: | ||
| 535 | /* int3 */ | ||
| 536 | *type = INSN_TRAP; | ||
| 537 | break; | ||
| 538 | |||
| 534 | case 0xe3: | 539 | case 0xe3: |
| 535 | /* jecxz/jrcxz */ | 540 | /* jecxz/jrcxz */ |
| 536 | *type = INSN_JUMP_CONDITIONAL; | 541 | *type = INSN_JUMP_CONDITIONAL; |
| @@ -697,10 +702,10 @@ const char *arch_ret_insn(int len) · 160 unmodified lines | |||
| 697 | { | 702 | { |
| 698 | static const char ret[5][5] = { | 703 | static const char ret[5][5] = { |
| 699 | { BYTE_RET }, | 704 | { BYTE_RET }, |
| 700 | { BYTE_RET, BYTES_NOP1 }, | 705 | { BYTE_RET, 0xcc }, |
| 701 | { BYTE_RET, BYTES_NOP2 }, | 706 | { BYTE_RET, 0xcc, BYTES_NOP1 }, |
| 702 | { BYTE_RET, BYTES_NOP3 }, | 707 | { BYTE_RET, 0xcc, BYTES_NOP2 }, |
| 703 | { BYTE_RET, BYTES_NOP4 }, | 708 | { BYTE_RET, 0xcc, BYTES_NOP3 }, |
| 704 | }; | 709 | }; |
| 705 | 710 | ||
| 706 | if (len < 1 || len > 5) { | 711 | if (len < 1 || len > 5) { |
| @@ -20,7 +20,7 @@ · 19 unmodified lines | |||
| 20 | #include <objtool/objtool.h> | 20 | #include <objtool/objtool.h> |
| 21 | 21 | ||
| 22 | bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, | 22 | bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, |
| 23 | validate_dup, vmlinux, mcount, noinstr, backup; | 23 | validate_dup, vmlinux, mcount, noinstr, backup, sls; |
| 24 | 24 | ||
| 25 | static const char * const check_usage[] = { | 25 | static const char * const check_usage[] = { |
| 26 | "objtool check [<options>] file.o", | 26 | "objtool check [<options>] file.o", |
| @@ -45,6 +45,7 @@ const struct option check_options[] = { · 18 unmodified lines | |||
| 45 | OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"), | 45 | OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"), |
| 46 | OPT_BOOLEAN('M', "mcount", &mcount, "generate __mcount_loc"), | 46 | OPT_BOOLEAN('M', "mcount", &mcount, "generate __mcount_loc"), |
| 47 | OPT_BOOLEAN('B', "backup", &backup, "create .orig files before modification"), | 47 | OPT_BOOLEAN('B', "backup", &backup, "create .orig files before modification"), |
| 48 | OPT_BOOLEAN('S', "sls", &sls, "validate straight-line-speculation"), | ||
| 48 | OPT_END(), | 49 | OPT_END(), |
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| @@ -3084,6 +3084,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, · 3083 unmodified lines | |||
| 3084 | switch (insn->type) { | 3084 | switch (insn->type) { |
| 3085 | 3085 | ||
| 3086 | case INSN_RETURN: | 3086 | case INSN_RETURN: |
| 3087 | if (next_insn && next_insn->type == INSN_TRAP) { | ||
| 3088 | next_insn->ignore = true; | ||
| 3089 | } else if (sls && !insn->retpoline_safe) { | ||
| 3090 | WARN_FUNC("missing int3 after ret", | ||
| 3091 | insn->sec, insn->offset); | ||
| 3092 | } | ||
| 3087 | return validate_return(func, insn, &state); | 3093 | return validate_return(func, insn, &state); |
| 3088 | 3094 | ||
| 3089 | case INSN_CALL: | 3095 | case INSN_CALL: |
| @@ -3127,6 +3133,14 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, · 37 unmodified lines | |||
| 3127 | break; | 3133 | break; |
| 3128 | 3134 | ||
| 3129 | case INSN_JUMP_DYNAMIC: | 3135 | case INSN_JUMP_DYNAMIC: |
| 3136 | if (next_insn && next_insn->type == INSN_TRAP) { | ||
| 3137 | next_insn->ignore = true; | ||
| 3138 | } else if (sls && !insn->retpoline_safe) { | ||
| 3139 | WARN_FUNC("missing int3 after indirect jump", | ||
| 3140 | insn->sec, insn->offset); | ||
| 3141 | } | ||
| 3142 | |||
| 3143 | /* fallthrough */ | ||
| 3130 | case INSN_JUMP_DYNAMIC_CONDITIONAL: | 3144 | case INSN_JUMP_DYNAMIC_CONDITIONAL: |
| 3131 | if (is_sibling_call(insn)) { | 3145 | if (is_sibling_call(insn)) { |
| 3132 | ret = validate_sibling_call(file, insn, &state); | 3146 | ret = validate_sibling_call(file, insn, &state); |
| @@ -26,6 +26,7 @@ enum insn_type { · 25 unmodified lines | |||
| 26 | INSN_CLAC, | 26 | INSN_CLAC, |
| 27 | INSN_STD, | 27 | INSN_STD, |
| 28 | INSN_CLD, | 28 | INSN_CLD, |
| 29 | INSN_TRAP, | ||
| 29 | INSN_OTHER, | 30 | INSN_OTHER, |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
| @@ -9,7 +9,7 @@ · 8 unmodified lines | |||
| 9 | 9 | ||
| 10 | extern const struct option check_options[]; | 10 | extern const struct option check_options[]; |
| 11 | extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, | 11 | extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, |
| 12 | validate_dup, vmlinux, mcount, noinstr, backup; | 12 | validate_dup, vmlinux, mcount, noinstr, backup, sls; |
| 13 | 13 | ||
| 14 | extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]); | 14 | extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]); |
| 15 | 15 | ||