diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/check_extable.sh | 146 | ||||
| -rw-r--r-- | scripts/mod/modpost.c | 341 |
2 files changed, 433 insertions, 54 deletions
diff --git a/scripts/check_extable.sh b/scripts/check_extable.sh new file mode 100755 index 000000000000..0fb6b1c97c27 --- /dev/null +++ b/scripts/check_extable.sh | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #! /bin/bash | ||
| 2 | # (c) 2015, Quentin Casasnovas <quentin.casasnovas@oracle.com> | ||
| 3 | |||
| 4 | obj=$1 | ||
| 5 | |||
| 6 | file ${obj} | grep -q ELF || (echo "${obj} is not and ELF file." 1>&2 ; exit 0) | ||
| 7 | |||
| 8 | # Bail out early if there isn't an __ex_table section in this object file. | ||
| 9 | objdump -hj __ex_table ${obj} 2> /dev/null > /dev/null | ||
| 10 | [ $? -ne 0 ] && exit 0 | ||
| 11 | |||
| 12 | white_list=.text,.fixup | ||
| 13 | |||
| 14 | suspicious_relocs=$(objdump -rj __ex_table ${obj} | tail -n +6 | | ||
| 15 | grep -v $(eval echo -e{${white_list}}) | awk '{print $3}') | ||
| 16 | |||
| 17 | # No suspicious relocs in __ex_table, jobs a good'un | ||
| 18 | [ -z "${suspicious_relocs}" ] && exit 0 | ||
| 19 | |||
| 20 | |||
| 21 | # After this point, something is seriously wrong since we just found out we | ||
| 22 | # have some relocations in __ex_table which point to sections which aren't | ||
| 23 | # white listed. If you're adding a new section in the Linux kernel, and | ||
| 24 | # you're expecting this section to contain code which can fault (i.e. the | ||
| 25 | # __ex_table relocation to your new section is expected), simply add your | ||
| 26 | # new section to the white_list variable above. If not, you're probably | ||
| 27 | # doing something wrong and the rest of this code is just trying to print | ||
| 28 | # you more information about it. | ||
| 29 | |||
| 30 | function find_section_offset_from_symbol() | ||
| 31 | { | ||
| 32 | eval $(objdump -t ${obj} | grep ${1} | sed 's/\([0-9a-f]\+\) .\{7\} \([^ \t]\+\).*/section="\2"; section_offset="0x\1" /') | ||
| 33 | |||
| 34 | # addr2line takes addresses in hexadecimal... | ||
| 35 | section_offset=$(printf "0x%016x" $(( ${section_offset} + $2 )) ) | ||
| 36 | } | ||
| 37 | |||
| 38 | function find_symbol_and_offset_from_reloc() | ||
| 39 | { | ||
| 40 | # Extract symbol and offset from the objdump output | ||
| 41 | eval $(echo $reloc | sed 's/\([^+]\+\)+\?\(0x[0-9a-f]\+\)\?/symbol="\1"; symbol_offset="\2"/') | ||
| 42 | |||
| 43 | # When the relocation points to the begining of a symbol or section, it | ||
| 44 | # won't print the offset since it is zero. | ||
| 45 | if [ -z "${symbol_offset}" ]; then | ||
| 46 | symbol_offset=0x0 | ||
| 47 | fi | ||
| 48 | } | ||
| 49 | |||
| 50 | function find_alt_replacement_target() | ||
| 51 | { | ||
| 52 | # The target of the .altinstr_replacement is the relocation just before | ||
| 53 | # the .altinstr_replacement one. | ||
| 54 | eval $(objdump -rj .altinstructions ${obj} | grep -B1 "${section}+${section_offset}" | head -n1 | awk '{print $3}' | | ||
| 55 | sed 's/\([^+]\+\)+\(0x[0-9a-f]\+\)/alt_target_section="\1"; alt_target_offset="\2"/') | ||
| 56 | } | ||
| 57 | |||
| 58 | function handle_alt_replacement_reloc() | ||
| 59 | { | ||
| 60 | # This will define alt_target_section and alt_target_section_offset | ||
| 61 | find_alt_replacement_target ${section} ${section_offset} | ||
| 62 | |||
| 63 | echo "Error: found a reference to .altinstr_replacement in __ex_table:" | ||
| 64 | addr2line -fip -j ${alt_target_section} -e ${obj} ${alt_target_offset} | awk '{print "\t" $0}' | ||
| 65 | |||
| 66 | error=true | ||
| 67 | } | ||
| 68 | |||
| 69 | function is_executable_section() | ||
| 70 | { | ||
| 71 | objdump -hwj ${section} ${obj} | grep -q CODE | ||
| 72 | return $? | ||
| 73 | } | ||
| 74 | |||
| 75 | function handle_suspicious_generic_reloc() | ||
| 76 | { | ||
| 77 | if is_executable_section ${section}; then | ||
| 78 | # We've got a relocation to a non white listed _executable_ | ||
| 79 | # section, print a warning so the developper adds the section to | ||
| 80 | # the white list or fix his code. We try to pretty-print the file | ||
| 81 | # and line number where that relocation was added. | ||
| 82 | echo "Warning: found a reference to section \"${section}\" in __ex_table:" | ||
| 83 | addr2line -fip -j ${section} -e ${obj} ${section_offset} | awk '{print "\t" $0}' | ||
| 84 | else | ||
| 85 | # Something is definitively wrong here since we've got a relocation | ||
| 86 | # to a non-executable section, there's no way this would ever be | ||
| 87 | # running in the kernel. | ||
| 88 | echo "Error: found a reference to non-executable section \"${section}\" in __ex_table at offset ${section_offset}" | ||
| 89 | error=true | ||
| 90 | fi | ||
| 91 | } | ||
| 92 | |||
| 93 | function handle_suspicious_reloc() | ||
| 94 | { | ||
| 95 | case "${section}" in | ||
| 96 | ".altinstr_replacement") | ||
| 97 | handle_alt_replacement_reloc ${section} ${section_offset} | ||
| 98 | ;; | ||
| 99 | *) | ||
| 100 | handle_suspicious_generic_reloc ${section} ${section_offset} | ||
| 101 | ;; | ||
| 102 | esac | ||
| 103 | } | ||
| 104 | |||
| 105 | function diagnose() | ||
| 106 | { | ||
| 107 | |||
| 108 | for reloc in ${suspicious_relocs}; do | ||
| 109 | # Let's find out where the target of the relocation in __ex_table | ||
| 110 | # is, this will define ${symbol} and ${symbol_offset} | ||
| 111 | find_symbol_and_offset_from_reloc ${reloc} | ||
| 112 | |||
| 113 | # When there's a global symbol at the place of the relocation, | ||
| 114 | # objdump will use it instead of giving us a section+offset, so | ||
| 115 | # let's find out which section is this symbol in and the total | ||
| 116 | # offset withing that section. | ||
| 117 | find_section_offset_from_symbol ${symbol} ${symbol_offset} | ||
| 118 | |||
| 119 | # In this case objdump was presenting us with a reloc to a symbol | ||
| 120 | # rather than a section. Now that we've got the actual section, | ||
| 121 | # we can skip it if it's in the white_list. | ||
| 122 | if [ -z "$( echo $section | grep -v $(eval echo -e{${white_list}}))" ]; then | ||
| 123 | continue; | ||
| 124 | fi | ||
| 125 | |||
| 126 | # Will either print a warning if the relocation happens to be in a | ||
| 127 | # section we do not know but has executable bit set, or error out. | ||
| 128 | handle_suspicious_reloc | ||
| 129 | done | ||
| 130 | } | ||
| 131 | |||
| 132 | function check_debug_info() { | ||
| 133 | objdump -hj .debug_info ${obj} 2> /dev/null > /dev/null || | ||
| 134 | echo -e "${obj} does not contain debug information, the addr2line output will be limited.\n" \ | ||
| 135 | "Recompile ${obj} with CONFIG_DEBUG_INFO to get a more useful output." | ||
| 136 | } | ||
| 137 | |||
| 138 | check_debug_info | ||
| 139 | |||
| 140 | diagnose | ||
| 141 | |||
| 142 | if [ "${error}" ]; then | ||
| 143 | exit 1 | ||
| 144 | fi | ||
| 145 | |||
| 146 | exit 0 | ||
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d439856f8176..91ee1b2e0f9a 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c | |||
| @@ -776,6 +776,7 @@ static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr) | |||
| 776 | * "foo" will match an exact string equal to "foo" | 776 | * "foo" will match an exact string equal to "foo" |
| 777 | * "*foo" will match a string that ends with "foo" | 777 | * "*foo" will match a string that ends with "foo" |
| 778 | * "foo*" will match a string that begins with "foo" | 778 | * "foo*" will match a string that begins with "foo" |
| 779 | * "*foo*" will match a string that contains "foo" | ||
| 779 | */ | 780 | */ |
| 780 | static int match(const char *sym, const char * const pat[]) | 781 | static int match(const char *sym, const char * const pat[]) |
| 781 | { | 782 | { |
| @@ -784,8 +785,17 @@ static int match(const char *sym, const char * const pat[]) | |||
| 784 | p = *pat++; | 785 | p = *pat++; |
| 785 | const char *endp = p + strlen(p) - 1; | 786 | const char *endp = p + strlen(p) - 1; |
| 786 | 787 | ||
| 788 | /* "*foo*" */ | ||
| 789 | if (*p == '*' && *endp == '*') { | ||
| 790 | char *here, *bare = strndup(p + 1, strlen(p) - 2); | ||
| 791 | |||
| 792 | here = strstr(sym, bare); | ||
| 793 | free(bare); | ||
| 794 | if (here != NULL) | ||
| 795 | return 1; | ||
| 796 | } | ||
| 787 | /* "*foo" */ | 797 | /* "*foo" */ |
| 788 | if (*p == '*') { | 798 | else if (*p == '*') { |
| 789 | if (strrcmp(sym, p + 1) == 0) | 799 | if (strrcmp(sym, p + 1) == 0) |
| 790 | return 1; | 800 | return 1; |
| 791 | } | 801 | } |
| @@ -873,7 +883,10 @@ static void check_section(const char *modname, struct elf_info *elf, | |||
| 873 | #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS | 883 | #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS |
| 874 | 884 | ||
| 875 | #define DATA_SECTIONS ".data", ".data.rel" | 885 | #define DATA_SECTIONS ".data", ".data.rel" |
| 876 | #define TEXT_SECTIONS ".text", ".text.unlikely" | 886 | #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \ |
| 887 | ".kprobes.text" | ||
| 888 | #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ | ||
| 889 | ".fixup", ".entry.text", ".exception.text", ".text.*" | ||
| 877 | 890 | ||
| 878 | #define INIT_SECTIONS ".init.*" | 891 | #define INIT_SECTIONS ".init.*" |
| 879 | #define MEM_INIT_SECTIONS ".meminit.*" | 892 | #define MEM_INIT_SECTIONS ".meminit.*" |
| @@ -881,6 +894,9 @@ static void check_section(const char *modname, struct elf_info *elf, | |||
| 881 | #define EXIT_SECTIONS ".exit.*" | 894 | #define EXIT_SECTIONS ".exit.*" |
| 882 | #define MEM_EXIT_SECTIONS ".memexit.*" | 895 | #define MEM_EXIT_SECTIONS ".memexit.*" |
| 883 | 896 | ||
| 897 | #define ALL_TEXT_SECTIONS ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \ | ||
| 898 | TEXT_SECTIONS, OTHER_TEXT_SECTIONS | ||
| 899 | |||
| 884 | /* init data sections */ | 900 | /* init data sections */ |
| 885 | static const char *const init_data_sections[] = | 901 | static const char *const init_data_sections[] = |
| 886 | { ALL_INIT_DATA_SECTIONS, NULL }; | 902 | { ALL_INIT_DATA_SECTIONS, NULL }; |
| @@ -892,6 +908,9 @@ static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL }; | |||
| 892 | static const char *const init_exit_sections[] = | 908 | static const char *const init_exit_sections[] = |
| 893 | {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }; | 909 | {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }; |
| 894 | 910 | ||
| 911 | /* all text sections */ | ||
| 912 | static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL }; | ||
| 913 | |||
| 895 | /* data section */ | 914 | /* data section */ |
| 896 | static const char *const data_sections[] = { DATA_SECTIONS, NULL }; | 915 | static const char *const data_sections[] = { DATA_SECTIONS, NULL }; |
| 897 | 916 | ||
| @@ -910,6 +929,7 @@ static const char *const data_sections[] = { DATA_SECTIONS, NULL }; | |||
| 910 | static const char *const head_sections[] = { ".head.text*", NULL }; | 929 | static const char *const head_sections[] = { ".head.text*", NULL }; |
| 911 | static const char *const linker_symbols[] = | 930 | static const char *const linker_symbols[] = |
| 912 | { "__init_begin", "_sinittext", "_einittext", NULL }; | 931 | { "__init_begin", "_sinittext", "_einittext", NULL }; |
| 932 | static const char *const optim_symbols[] = { "*.constprop.*", NULL }; | ||
| 913 | 933 | ||
| 914 | enum mismatch { | 934 | enum mismatch { |
| 915 | TEXT_TO_ANY_INIT, | 935 | TEXT_TO_ANY_INIT, |
| @@ -921,34 +941,65 @@ enum mismatch { | |||
| 921 | ANY_INIT_TO_ANY_EXIT, | 941 | ANY_INIT_TO_ANY_EXIT, |
| 922 | ANY_EXIT_TO_ANY_INIT, | 942 | ANY_EXIT_TO_ANY_INIT, |
| 923 | EXPORT_TO_INIT_EXIT, | 943 | EXPORT_TO_INIT_EXIT, |
| 944 | EXTABLE_TO_NON_TEXT, | ||
| 924 | }; | 945 | }; |
| 925 | 946 | ||
| 947 | /** | ||
| 948 | * Describe how to match sections on different criterias: | ||
| 949 | * | ||
| 950 | * @fromsec: Array of sections to be matched. | ||
| 951 | * | ||
| 952 | * @bad_tosec: Relocations applied to a section in @fromsec to a section in | ||
| 953 | * this array is forbidden (black-list). Can be empty. | ||
| 954 | * | ||
| 955 | * @good_tosec: Relocations applied to a section in @fromsec must be | ||
| 956 | * targetting sections in this array (white-list). Can be empty. | ||
| 957 | * | ||
| 958 | * @mismatch: Type of mismatch. | ||
| 959 | * | ||
| 960 | * @symbol_white_list: Do not match a relocation to a symbol in this list | ||
| 961 | * even if it is targetting a section in @bad_to_sec. | ||
| 962 | * | ||
| 963 | * @handler: Specific handler to call when a match is found. If NULL, | ||
| 964 | * default_mismatch_handler() will be called. | ||
| 965 | * | ||
| 966 | */ | ||
| 926 | struct sectioncheck { | 967 | struct sectioncheck { |
| 927 | const char *fromsec[20]; | 968 | const char *fromsec[20]; |
| 928 | const char *tosec[20]; | 969 | const char *bad_tosec[20]; |
| 970 | const char *good_tosec[20]; | ||
| 929 | enum mismatch mismatch; | 971 | enum mismatch mismatch; |
| 930 | const char *symbol_white_list[20]; | 972 | const char *symbol_white_list[20]; |
| 973 | void (*handler)(const char *modname, struct elf_info *elf, | ||
| 974 | const struct sectioncheck* const mismatch, | ||
| 975 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec); | ||
| 976 | |||
| 931 | }; | 977 | }; |
| 932 | 978 | ||
| 979 | static void extable_mismatch_handler(const char *modname, struct elf_info *elf, | ||
| 980 | const struct sectioncheck* const mismatch, | ||
| 981 | Elf_Rela *r, Elf_Sym *sym, | ||
| 982 | const char *fromsec); | ||
| 983 | |||
| 933 | static const struct sectioncheck sectioncheck[] = { | 984 | static const struct sectioncheck sectioncheck[] = { |
| 934 | /* Do not reference init/exit code/data from | 985 | /* Do not reference init/exit code/data from |
| 935 | * normal code and data | 986 | * normal code and data |
| 936 | */ | 987 | */ |
| 937 | { | 988 | { |
| 938 | .fromsec = { TEXT_SECTIONS, NULL }, | 989 | .fromsec = { TEXT_SECTIONS, NULL }, |
| 939 | .tosec = { ALL_INIT_SECTIONS, NULL }, | 990 | .bad_tosec = { ALL_INIT_SECTIONS, NULL }, |
| 940 | .mismatch = TEXT_TO_ANY_INIT, | 991 | .mismatch = TEXT_TO_ANY_INIT, |
| 941 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 992 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 942 | }, | 993 | }, |
| 943 | { | 994 | { |
| 944 | .fromsec = { DATA_SECTIONS, NULL }, | 995 | .fromsec = { DATA_SECTIONS, NULL }, |
| 945 | .tosec = { ALL_XXXINIT_SECTIONS, NULL }, | 996 | .bad_tosec = { ALL_XXXINIT_SECTIONS, NULL }, |
| 946 | .mismatch = DATA_TO_ANY_INIT, | 997 | .mismatch = DATA_TO_ANY_INIT, |
| 947 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 998 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 948 | }, | 999 | }, |
| 949 | { | 1000 | { |
| 950 | .fromsec = { DATA_SECTIONS, NULL }, | 1001 | .fromsec = { DATA_SECTIONS, NULL }, |
| 951 | .tosec = { INIT_SECTIONS, NULL }, | 1002 | .bad_tosec = { INIT_SECTIONS, NULL }, |
| 952 | .mismatch = DATA_TO_ANY_INIT, | 1003 | .mismatch = DATA_TO_ANY_INIT, |
| 953 | .symbol_white_list = { | 1004 | .symbol_white_list = { |
| 954 | "*_template", "*_timer", "*_sht", "*_ops", | 1005 | "*_template", "*_timer", "*_sht", "*_ops", |
| @@ -957,56 +1008,66 @@ static const struct sectioncheck sectioncheck[] = { | |||
| 957 | }, | 1008 | }, |
| 958 | { | 1009 | { |
| 959 | .fromsec = { TEXT_SECTIONS, NULL }, | 1010 | .fromsec = { TEXT_SECTIONS, NULL }, |
| 960 | .tosec = { ALL_EXIT_SECTIONS, NULL }, | 1011 | .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, |
| 961 | .mismatch = TEXT_TO_ANY_EXIT, | 1012 | .mismatch = TEXT_TO_ANY_EXIT, |
| 962 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1013 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 963 | }, | 1014 | }, |
| 964 | { | 1015 | { |
| 965 | .fromsec = { DATA_SECTIONS, NULL }, | 1016 | .fromsec = { DATA_SECTIONS, NULL }, |
| 966 | .tosec = { ALL_EXIT_SECTIONS, NULL }, | 1017 | .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, |
| 967 | .mismatch = DATA_TO_ANY_EXIT, | 1018 | .mismatch = DATA_TO_ANY_EXIT, |
| 968 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1019 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 969 | }, | 1020 | }, |
| 970 | /* Do not reference init code/data from meminit code/data */ | 1021 | /* Do not reference init code/data from meminit code/data */ |
| 971 | { | 1022 | { |
| 972 | .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, | 1023 | .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, |
| 973 | .tosec = { INIT_SECTIONS, NULL }, | 1024 | .bad_tosec = { INIT_SECTIONS, NULL }, |
| 974 | .mismatch = XXXINIT_TO_SOME_INIT, | 1025 | .mismatch = XXXINIT_TO_SOME_INIT, |
| 975 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1026 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 976 | }, | 1027 | }, |
| 977 | /* Do not reference exit code/data from memexit code/data */ | 1028 | /* Do not reference exit code/data from memexit code/data */ |
| 978 | { | 1029 | { |
| 979 | .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, | 1030 | .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, |
| 980 | .tosec = { EXIT_SECTIONS, NULL }, | 1031 | .bad_tosec = { EXIT_SECTIONS, NULL }, |
| 981 | .mismatch = XXXEXIT_TO_SOME_EXIT, | 1032 | .mismatch = XXXEXIT_TO_SOME_EXIT, |
| 982 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1033 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 983 | }, | 1034 | }, |
| 984 | /* Do not use exit code/data from init code */ | 1035 | /* Do not use exit code/data from init code */ |
| 985 | { | 1036 | { |
| 986 | .fromsec = { ALL_INIT_SECTIONS, NULL }, | 1037 | .fromsec = { ALL_INIT_SECTIONS, NULL }, |
| 987 | .tosec = { ALL_EXIT_SECTIONS, NULL }, | 1038 | .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, |
| 988 | .mismatch = ANY_INIT_TO_ANY_EXIT, | 1039 | .mismatch = ANY_INIT_TO_ANY_EXIT, |
| 989 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1040 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 990 | }, | 1041 | }, |
| 991 | /* Do not use init code/data from exit code */ | 1042 | /* Do not use init code/data from exit code */ |
| 992 | { | 1043 | { |
| 993 | .fromsec = { ALL_EXIT_SECTIONS, NULL }, | 1044 | .fromsec = { ALL_EXIT_SECTIONS, NULL }, |
| 994 | .tosec = { ALL_INIT_SECTIONS, NULL }, | 1045 | .bad_tosec = { ALL_INIT_SECTIONS, NULL }, |
| 995 | .mismatch = ANY_EXIT_TO_ANY_INIT, | 1046 | .mismatch = ANY_EXIT_TO_ANY_INIT, |
| 996 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1047 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 997 | }, | 1048 | }, |
| 998 | { | 1049 | { |
| 999 | .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, | 1050 | .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, |
| 1000 | .tosec = { INIT_SECTIONS, NULL }, | 1051 | .bad_tosec = { INIT_SECTIONS, NULL }, |
| 1001 | .mismatch = ANY_INIT_TO_ANY_EXIT, | 1052 | .mismatch = ANY_INIT_TO_ANY_EXIT, |
| 1002 | .symbol_white_list = { NULL }, | 1053 | .symbol_white_list = { NULL }, |
| 1003 | }, | 1054 | }, |
| 1004 | /* Do not export init/exit functions or data */ | 1055 | /* Do not export init/exit functions or data */ |
| 1005 | { | 1056 | { |
| 1006 | .fromsec = { "__ksymtab*", NULL }, | 1057 | .fromsec = { "__ksymtab*", NULL }, |
| 1007 | .tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, | 1058 | .bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, |
| 1008 | .mismatch = EXPORT_TO_INIT_EXIT, | 1059 | .mismatch = EXPORT_TO_INIT_EXIT, |
| 1009 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, | 1060 | .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, |
| 1061 | }, | ||
| 1062 | { | ||
| 1063 | .fromsec = { "__ex_table", NULL }, | ||
| 1064 | /* If you're adding any new black-listed sections in here, consider | ||
| 1065 | * adding a special 'printer' for them in scripts/check_extable. | ||
| 1066 | */ | ||
| 1067 | .bad_tosec = { ".altinstr_replacement", NULL }, | ||
| 1068 | .good_tosec = {ALL_TEXT_SECTIONS , NULL}, | ||
| 1069 | .mismatch = EXTABLE_TO_NON_TEXT, | ||
| 1070 | .handler = extable_mismatch_handler, | ||
| 1010 | } | 1071 | } |
| 1011 | }; | 1072 | }; |
| 1012 | 1073 | ||
| @@ -1017,10 +1078,22 @@ static const struct sectioncheck *section_mismatch( | |||
| 1017 | int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck); | 1078 | int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck); |
| 1018 | const struct sectioncheck *check = §ioncheck[0]; | 1079 | const struct sectioncheck *check = §ioncheck[0]; |
| 1019 | 1080 | ||
| 1081 | /* | ||
| 1082 | * The target section could be the SHT_NUL section when we're | ||
| 1083 | * handling relocations to un-resolved symbols, trying to match it | ||
| 1084 | * doesn't make much sense and causes build failures on parisc and | ||
| 1085 | * mn10300 architectures. | ||
| 1086 | */ | ||
| 1087 | if (*tosec == '\0') | ||
| 1088 | return NULL; | ||
| 1089 | |||
| 1020 | for (i = 0; i < elems; i++) { | 1090 | for (i = 0; i < elems; i++) { |
| 1021 | if (match(fromsec, check->fromsec) && | 1091 | if (match(fromsec, check->fromsec)) { |
| 1022 | match(tosec, check->tosec)) | 1092 | if (check->bad_tosec[0] && match(tosec, check->bad_tosec)) |
| 1023 | return check; | 1093 | return check; |
| 1094 | if (check->good_tosec[0] && !match(tosec, check->good_tosec)) | ||
| 1095 | return check; | ||
| 1096 | } | ||
| 1024 | check++; | 1097 | check++; |
| 1025 | } | 1098 | } |
| 1026 | return NULL; | 1099 | return NULL; |
| @@ -1067,6 +1140,17 @@ static const struct sectioncheck *section_mismatch( | |||
| 1067 | * This pattern is identified by | 1140 | * This pattern is identified by |
| 1068 | * refsymname = __init_begin, _sinittext, _einittext | 1141 | * refsymname = __init_begin, _sinittext, _einittext |
| 1069 | * | 1142 | * |
| 1143 | * Pattern 5: | ||
| 1144 | * GCC may optimize static inlines when fed constant arg(s) resulting | ||
| 1145 | * in functions like cpumask_empty() -- generating an associated symbol | ||
| 1146 | * cpumask_empty.constprop.3 that appears in the audit. If the const that | ||
| 1147 | * is passed in comes from __init, like say nmi_ipi_mask, we get a | ||
| 1148 | * meaningless section warning. May need to add isra symbols too... | ||
| 1149 | * This pattern is identified by | ||
| 1150 | * tosec = init section | ||
| 1151 | * fromsec = text section | ||
| 1152 | * refsymname = *.constprop.* | ||
| 1153 | * | ||
| 1070 | **/ | 1154 | **/ |
| 1071 | static int secref_whitelist(const struct sectioncheck *mismatch, | 1155 | static int secref_whitelist(const struct sectioncheck *mismatch, |
| 1072 | const char *fromsec, const char *fromsym, | 1156 | const char *fromsec, const char *fromsym, |
| @@ -1099,6 +1183,12 @@ static int secref_whitelist(const struct sectioncheck *mismatch, | |||
| 1099 | if (match(tosym, linker_symbols)) | 1183 | if (match(tosym, linker_symbols)) |
| 1100 | return 0; | 1184 | return 0; |
| 1101 | 1185 | ||
| 1186 | /* Check for pattern 5 */ | ||
| 1187 | if (match(fromsec, text_sections) && | ||
| 1188 | match(tosec, init_sections) && | ||
| 1189 | match(fromsym, optim_symbols)) | ||
| 1190 | return 0; | ||
| 1191 | |||
| 1102 | return 1; | 1192 | return 1; |
| 1103 | } | 1193 | } |
| 1104 | 1194 | ||
| @@ -1261,6 +1351,15 @@ static void print_section_list(const char * const list[20]) | |||
| 1261 | fprintf(stderr, "\n"); | 1351 | fprintf(stderr, "\n"); |
| 1262 | } | 1352 | } |
| 1263 | 1353 | ||
| 1354 | static inline void get_pretty_name(int is_func, const char** name, const char** name_p) | ||
| 1355 | { | ||
| 1356 | switch (is_func) { | ||
| 1357 | case 0: *name = "variable"; *name_p = ""; break; | ||
| 1358 | case 1: *name = "function"; *name_p = "()"; break; | ||
| 1359 | default: *name = "(unknown reference)"; *name_p = ""; break; | ||
| 1360 | } | ||
| 1361 | } | ||
| 1362 | |||
| 1264 | /* | 1363 | /* |
| 1265 | * Print a warning about a section mismatch. | 1364 | * Print a warning about a section mismatch. |
| 1266 | * Try to find symbols near it so user can find it. | 1365 | * Try to find symbols near it so user can find it. |
| @@ -1280,21 +1379,13 @@ static void report_sec_mismatch(const char *modname, | |||
| 1280 | char *prl_from; | 1379 | char *prl_from; |
| 1281 | char *prl_to; | 1380 | char *prl_to; |
| 1282 | 1381 | ||
| 1283 | switch (from_is_func) { | ||
| 1284 | case 0: from = "variable"; from_p = ""; break; | ||
| 1285 | case 1: from = "function"; from_p = "()"; break; | ||
| 1286 | default: from = "(unknown reference)"; from_p = ""; break; | ||
| 1287 | } | ||
| 1288 | switch (to_is_func) { | ||
| 1289 | case 0: to = "variable"; to_p = ""; break; | ||
| 1290 | case 1: to = "function"; to_p = "()"; break; | ||
| 1291 | default: to = "(unknown reference)"; to_p = ""; break; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | sec_mismatch_count++; | 1382 | sec_mismatch_count++; |
| 1295 | if (!sec_mismatch_verbose) | 1383 | if (!sec_mismatch_verbose) |
| 1296 | return; | 1384 | return; |
| 1297 | 1385 | ||
| 1386 | get_pretty_name(from_is_func, &from, &from_p); | ||
| 1387 | get_pretty_name(to_is_func, &to, &to_p); | ||
| 1388 | |||
| 1298 | warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s " | 1389 | warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s " |
| 1299 | "to the %s %s:%s%s\n", | 1390 | "to the %s %s:%s%s\n", |
| 1300 | modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec, | 1391 | modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec, |
| @@ -1408,41 +1499,179 @@ static void report_sec_mismatch(const char *modname, | |||
| 1408 | tosym, prl_to, prl_to, tosym); | 1499 | tosym, prl_to, prl_to, tosym); |
| 1409 | free(prl_to); | 1500 | free(prl_to); |
| 1410 | break; | 1501 | break; |
| 1502 | case EXTABLE_TO_NON_TEXT: | ||
| 1503 | fatal("There's a special handler for this mismatch type, " | ||
| 1504 | "we should never get here."); | ||
| 1505 | break; | ||
| 1411 | } | 1506 | } |
| 1412 | fprintf(stderr, "\n"); | 1507 | fprintf(stderr, "\n"); |
| 1413 | } | 1508 | } |
| 1414 | 1509 | ||
| 1415 | static void check_section_mismatch(const char *modname, struct elf_info *elf, | 1510 | static void default_mismatch_handler(const char *modname, struct elf_info *elf, |
| 1416 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) | 1511 | const struct sectioncheck* const mismatch, |
| 1512 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) | ||
| 1417 | { | 1513 | { |
| 1418 | const char *tosec; | 1514 | const char *tosec; |
| 1419 | const struct sectioncheck *mismatch; | 1515 | Elf_Sym *to; |
| 1516 | Elf_Sym *from; | ||
| 1517 | const char *tosym; | ||
| 1518 | const char *fromsym; | ||
| 1519 | |||
| 1520 | from = find_elf_symbol2(elf, r->r_offset, fromsec); | ||
| 1521 | fromsym = sym_name(elf, from); | ||
| 1522 | |||
| 1523 | if (!strncmp(fromsym, "reference___initcall", | ||
| 1524 | sizeof("reference___initcall")-1)) | ||
| 1525 | return; | ||
| 1420 | 1526 | ||
| 1421 | tosec = sec_name(elf, get_secindex(elf, sym)); | 1527 | tosec = sec_name(elf, get_secindex(elf, sym)); |
| 1422 | mismatch = section_mismatch(fromsec, tosec); | 1528 | to = find_elf_symbol(elf, r->r_addend, sym); |
| 1529 | tosym = sym_name(elf, to); | ||
| 1530 | |||
| 1531 | /* check whitelist - we may ignore it */ | ||
| 1532 | if (secref_whitelist(mismatch, | ||
| 1533 | fromsec, fromsym, tosec, tosym)) { | ||
| 1534 | report_sec_mismatch(modname, mismatch, | ||
| 1535 | fromsec, r->r_offset, fromsym, | ||
| 1536 | is_function(from), tosec, tosym, | ||
| 1537 | is_function(to)); | ||
| 1538 | } | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | static int is_executable_section(struct elf_info* elf, unsigned int section_index) | ||
| 1542 | { | ||
| 1543 | if (section_index > elf->num_sections) | ||
| 1544 | fatal("section_index is outside elf->num_sections!\n"); | ||
| 1545 | |||
| 1546 | return ((elf->sechdrs[section_index].sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | /* | ||
| 1550 | * We rely on a gross hack in section_rel[a]() calling find_extable_entry_size() | ||
| 1551 | * to know the sizeof(struct exception_table_entry) for the target architecture. | ||
| 1552 | */ | ||
| 1553 | static unsigned int extable_entry_size = 0; | ||
| 1554 | static void find_extable_entry_size(const char* const sec, const Elf_Rela* r) | ||
| 1555 | { | ||
| 1556 | /* | ||
| 1557 | * If we're currently checking the second relocation within __ex_table, | ||
| 1558 | * that relocation offset tells us the offsetof(struct | ||
| 1559 | * exception_table_entry, fixup) which is equal to sizeof(struct | ||
| 1560 | * exception_table_entry) divided by two. We use that to our advantage | ||
| 1561 | * since there's no portable way to get that size as every architecture | ||
| 1562 | * seems to go with different sized types. Not pretty but better than | ||
| 1563 | * hard-coding the size for every architecture.. | ||
| 1564 | */ | ||
| 1565 | if (!extable_entry_size) | ||
| 1566 | extable_entry_size = r->r_offset * 2; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | static inline bool is_extable_fault_address(Elf_Rela *r) | ||
| 1570 | { | ||
| 1571 | /* | ||
| 1572 | * extable_entry_size is only discovered after we've handled the | ||
| 1573 | * _second_ relocation in __ex_table, so only abort when we're not | ||
| 1574 | * handling the first reloc and extable_entry_size is zero. | ||
| 1575 | */ | ||
| 1576 | if (r->r_offset && extable_entry_size == 0) | ||
| 1577 | fatal("extable_entry size hasn't been discovered!\n"); | ||
| 1578 | |||
| 1579 | return ((r->r_offset == 0) || | ||
| 1580 | (r->r_offset % extable_entry_size == 0)); | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | #define is_second_extable_reloc(Start, Cur, Sec) \ | ||
| 1584 | (((Cur) == (Start) + 1) && (strcmp("__ex_table", (Sec)) == 0)) | ||
| 1585 | |||
| 1586 | static void report_extable_warnings(const char* modname, struct elf_info* elf, | ||
| 1587 | const struct sectioncheck* const mismatch, | ||
| 1588 | Elf_Rela* r, Elf_Sym* sym, | ||
| 1589 | const char* fromsec, const char* tosec) | ||
| 1590 | { | ||
| 1591 | Elf_Sym* fromsym = find_elf_symbol2(elf, r->r_offset, fromsec); | ||
| 1592 | const char* fromsym_name = sym_name(elf, fromsym); | ||
| 1593 | Elf_Sym* tosym = find_elf_symbol(elf, r->r_addend, sym); | ||
| 1594 | const char* tosym_name = sym_name(elf, tosym); | ||
| 1595 | const char* from_pretty_name; | ||
| 1596 | const char* from_pretty_name_p; | ||
| 1597 | const char* to_pretty_name; | ||
| 1598 | const char* to_pretty_name_p; | ||
| 1599 | |||
| 1600 | get_pretty_name(is_function(fromsym), | ||
| 1601 | &from_pretty_name, &from_pretty_name_p); | ||
| 1602 | get_pretty_name(is_function(tosym), | ||
| 1603 | &to_pretty_name, &to_pretty_name_p); | ||
| 1604 | |||
| 1605 | warn("%s(%s+0x%lx): Section mismatch in reference" | ||
| 1606 | " from the %s %s%s to the %s %s:%s%s\n", | ||
| 1607 | modname, fromsec, (long)r->r_offset, from_pretty_name, | ||
| 1608 | fromsym_name, from_pretty_name_p, | ||
| 1609 | to_pretty_name, tosec, tosym_name, to_pretty_name_p); | ||
| 1610 | |||
| 1611 | if (!match(tosec, mismatch->bad_tosec) && | ||
| 1612 | is_executable_section(elf, get_secindex(elf, sym))) | ||
| 1613 | fprintf(stderr, | ||
| 1614 | "The relocation at %s+0x%lx references\n" | ||
| 1615 | "section \"%s\" which is not in the list of\n" | ||
| 1616 | "authorized sections. If you're adding a new section\n" | ||
| 1617 | "and/or if this reference is valid, add \"%s\" to the\n" | ||
| 1618 | "list of authorized sections to jump to on fault.\n" | ||
| 1619 | "This can be achieved by adding \"%s\" to \n" | ||
| 1620 | "OTHER_TEXT_SECTIONS in scripts/mod/modpost.c.\n", | ||
| 1621 | fromsec, (long)r->r_offset, tosec, tosec, tosec); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | static void extable_mismatch_handler(const char* modname, struct elf_info *elf, | ||
| 1625 | const struct sectioncheck* const mismatch, | ||
| 1626 | Elf_Rela* r, Elf_Sym* sym, | ||
| 1627 | const char *fromsec) | ||
| 1628 | { | ||
| 1629 | const char* tosec = sec_name(elf, get_secindex(elf, sym)); | ||
| 1630 | |||
| 1631 | sec_mismatch_count++; | ||
| 1632 | |||
| 1633 | if (sec_mismatch_verbose) | ||
| 1634 | report_extable_warnings(modname, elf, mismatch, r, sym, | ||
| 1635 | fromsec, tosec); | ||
| 1636 | |||
| 1637 | if (match(tosec, mismatch->bad_tosec)) | ||
| 1638 | fatal("The relocation at %s+0x%lx references\n" | ||
| 1639 | "section \"%s\" which is black-listed.\n" | ||
| 1640 | "Something is seriously wrong and should be fixed.\n" | ||
| 1641 | "You might get more information about where this is\n" | ||
| 1642 | "coming from by using scripts/check_extable.sh %s\n", | ||
| 1643 | fromsec, (long)r->r_offset, tosec, modname); | ||
| 1644 | else if (!is_executable_section(elf, get_secindex(elf, sym))) { | ||
| 1645 | if (is_extable_fault_address(r)) | ||
| 1646 | fatal("The relocation at %s+0x%lx references\n" | ||
| 1647 | "section \"%s\" which is not executable, IOW\n" | ||
| 1648 | "it is not possible for the kernel to fault\n" | ||
| 1649 | "at that address. Something is seriously wrong\n" | ||
| 1650 | "and should be fixed.\n", | ||
| 1651 | fromsec, (long)r->r_offset, tosec); | ||
| 1652 | else | ||
| 1653 | fatal("The relocation at %s+0x%lx references\n" | ||
| 1654 | "section \"%s\" which is not executable, IOW\n" | ||
| 1655 | "the kernel will fault if it ever tries to\n" | ||
| 1656 | "jump to it. Something is seriously wrong\n" | ||
| 1657 | "and should be fixed.\n", | ||
| 1658 | fromsec, (long)r->r_offset, tosec); | ||
| 1659 | } | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | static void check_section_mismatch(const char *modname, struct elf_info *elf, | ||
| 1663 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) | ||
| 1664 | { | ||
| 1665 | const char *tosec = sec_name(elf, get_secindex(elf, sym));; | ||
| 1666 | const struct sectioncheck *mismatch = section_mismatch(fromsec, tosec); | ||
| 1667 | |||
| 1423 | if (mismatch) { | 1668 | if (mismatch) { |
| 1424 | Elf_Sym *to; | 1669 | if (mismatch->handler) |
| 1425 | Elf_Sym *from; | 1670 | mismatch->handler(modname, elf, mismatch, |
| 1426 | const char *tosym; | 1671 | r, sym, fromsec); |
| 1427 | const char *fromsym; | 1672 | else |
| 1428 | 1673 | default_mismatch_handler(modname, elf, mismatch, | |
| 1429 | from = find_elf_symbol2(elf, r->r_offset, fromsec); | 1674 | r, sym, fromsec); |
| 1430 | fromsym = sym_name(elf, from); | ||
| 1431 | to = find_elf_symbol(elf, r->r_addend, sym); | ||
| 1432 | tosym = sym_name(elf, to); | ||
| 1433 | |||
| 1434 | if (!strncmp(fromsym, "reference___initcall", | ||
| 1435 | sizeof("reference___initcall")-1)) | ||
| 1436 | return; | ||
| 1437 | |||
| 1438 | /* check whitelist - we may ignore it */ | ||
| 1439 | if (secref_whitelist(mismatch, | ||
| 1440 | fromsec, fromsym, tosec, tosym)) { | ||
| 1441 | report_sec_mismatch(modname, mismatch, | ||
| 1442 | fromsec, r->r_offset, fromsym, | ||
| 1443 | is_function(from), tosec, tosym, | ||
| 1444 | is_function(to)); | ||
| 1445 | } | ||
| 1446 | } | 1675 | } |
| 1447 | } | 1676 | } |
| 1448 | 1677 | ||
| @@ -1582,6 +1811,8 @@ static void section_rela(const char *modname, struct elf_info *elf, | |||
| 1582 | /* Skip special sections */ | 1811 | /* Skip special sections */ |
| 1583 | if (is_shndx_special(sym->st_shndx)) | 1812 | if (is_shndx_special(sym->st_shndx)) |
| 1584 | continue; | 1813 | continue; |
| 1814 | if (is_second_extable_reloc(start, rela, fromsec)) | ||
| 1815 | find_extable_entry_size(fromsec, &r); | ||
| 1585 | check_section_mismatch(modname, elf, &r, sym, fromsec); | 1816 | check_section_mismatch(modname, elf, &r, sym, fromsec); |
| 1586 | } | 1817 | } |
| 1587 | } | 1818 | } |
| @@ -1640,6 +1871,8 @@ static void section_rel(const char *modname, struct elf_info *elf, | |||
| 1640 | /* Skip special sections */ | 1871 | /* Skip special sections */ |
| 1641 | if (is_shndx_special(sym->st_shndx)) | 1872 | if (is_shndx_special(sym->st_shndx)) |
| 1642 | continue; | 1873 | continue; |
| 1874 | if (is_second_extable_reloc(start, rel, fromsec)) | ||
| 1875 | find_extable_entry_size(fromsec, &r); | ||
| 1643 | check_section_mismatch(modname, elf, &r, sym, fromsec); | 1876 | check_section_mismatch(modname, elf, &r, sym, fromsec); |
| 1644 | } | 1877 | } |
| 1645 | } | 1878 | } |
