diff options
Diffstat (limited to 'kernel/unwind.c')
-rw-r--r-- | kernel/unwind.c | 203 |
1 files changed, 163 insertions, 40 deletions
diff --git a/kernel/unwind.c b/kernel/unwind.c index ed0a21d4a902..09c261329249 100644 --- a/kernel/unwind.c +++ b/kernel/unwind.c | |||
@@ -14,11 +14,12 @@ | |||
14 | #include <linux/bootmem.h> | 14 | #include <linux/bootmem.h> |
15 | #include <linux/sort.h> | 15 | #include <linux/sort.h> |
16 | #include <linux/stop_machine.h> | 16 | #include <linux/stop_machine.h> |
17 | #include <linux/uaccess.h> | ||
17 | #include <asm/sections.h> | 18 | #include <asm/sections.h> |
18 | #include <asm/uaccess.h> | 19 | #include <asm/uaccess.h> |
19 | #include <asm/unaligned.h> | 20 | #include <asm/unaligned.h> |
20 | 21 | ||
21 | extern char __start_unwind[], __end_unwind[]; | 22 | extern const char __start_unwind[], __end_unwind[]; |
22 | extern const u8 __start_unwind_hdr[], __end_unwind_hdr[]; | 23 | extern const u8 __start_unwind_hdr[], __end_unwind_hdr[]; |
23 | 24 | ||
24 | #define MAX_STACK_DEPTH 8 | 25 | #define MAX_STACK_DEPTH 8 |
@@ -94,6 +95,7 @@ static const struct { | |||
94 | 95 | ||
95 | typedef unsigned long uleb128_t; | 96 | typedef unsigned long uleb128_t; |
96 | typedef signed long sleb128_t; | 97 | typedef signed long sleb128_t; |
98 | #define sleb128abs __builtin_labs | ||
97 | 99 | ||
98 | static struct unwind_table { | 100 | static struct unwind_table { |
99 | struct { | 101 | struct { |
@@ -135,6 +137,17 @@ struct unwind_state { | |||
135 | 137 | ||
136 | static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; | 138 | static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; |
137 | 139 | ||
140 | static unsigned unwind_debug; | ||
141 | static int __init unwind_debug_setup(char *s) | ||
142 | { | ||
143 | unwind_debug = simple_strtoul(s, NULL, 0); | ||
144 | return 1; | ||
145 | } | ||
146 | __setup("unwind_debug=", unwind_debug_setup); | ||
147 | #define dprintk(lvl, fmt, args...) \ | ||
148 | ((void)(lvl > unwind_debug \ | ||
149 | || printk(KERN_DEBUG "unwind: " fmt "\n", ##args))) | ||
150 | |||
138 | static struct unwind_table *find_table(unsigned long pc) | 151 | static struct unwind_table *find_table(unsigned long pc) |
139 | { | 152 | { |
140 | struct unwind_table *table; | 153 | struct unwind_table *table; |
@@ -151,7 +164,9 @@ static struct unwind_table *find_table(unsigned long pc) | |||
151 | 164 | ||
152 | static unsigned long read_pointer(const u8 **pLoc, | 165 | static unsigned long read_pointer(const u8 **pLoc, |
153 | const void *end, | 166 | const void *end, |
154 | signed ptrType); | 167 | signed ptrType, |
168 | unsigned long text_base, | ||
169 | unsigned long data_base); | ||
155 | 170 | ||
156 | static void init_unwind_table(struct unwind_table *table, | 171 | static void init_unwind_table(struct unwind_table *table, |
157 | const char *name, | 172 | const char *name, |
@@ -176,10 +191,13 @@ static void init_unwind_table(struct unwind_table *table, | |||
176 | /* See if the linker provided table looks valid. */ | 191 | /* See if the linker provided table looks valid. */ |
177 | if (header_size <= 4 | 192 | if (header_size <= 4 |
178 | || header_start[0] != 1 | 193 | || header_start[0] != 1 |
179 | || (void *)read_pointer(&ptr, end, header_start[1]) != table_start | 194 | || (void *)read_pointer(&ptr, end, header_start[1], 0, 0) |
180 | || header_start[2] == DW_EH_PE_omit | 195 | != table_start |
181 | || read_pointer(&ptr, end, header_start[2]) <= 0 | 196 | || !read_pointer(&ptr, end, header_start[2], 0, 0) |
182 | || header_start[3] == DW_EH_PE_omit) | 197 | || !read_pointer(&ptr, end, header_start[3], 0, |
198 | (unsigned long)header_start) | ||
199 | || !read_pointer(&ptr, end, header_start[3], 0, | ||
200 | (unsigned long)header_start)) | ||
183 | header_start = NULL; | 201 | header_start = NULL; |
184 | table->hdrsz = header_size; | 202 | table->hdrsz = header_size; |
185 | smp_wmb(); | 203 | smp_wmb(); |
@@ -269,7 +287,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
269 | ptr = (const u8 *)(fde + 2); | 287 | ptr = (const u8 *)(fde + 2); |
270 | if (!read_pointer(&ptr, | 288 | if (!read_pointer(&ptr, |
271 | (const u8 *)(fde + 1) + *fde, | 289 | (const u8 *)(fde + 1) + *fde, |
272 | ptrType)) | 290 | ptrType, 0, 0)) |
273 | return; | 291 | return; |
274 | ++n; | 292 | ++n; |
275 | } | 293 | } |
@@ -279,6 +297,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
279 | 297 | ||
280 | hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) | 298 | hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) |
281 | + 2 * n * sizeof(unsigned long); | 299 | + 2 * n * sizeof(unsigned long); |
300 | dprintk(2, "Binary lookup table size for %s: %lu bytes", table->name, hdrSize); | ||
282 | header = alloc(hdrSize); | 301 | header = alloc(hdrSize); |
283 | if (!header) | 302 | if (!header) |
284 | return; | 303 | return; |
@@ -303,7 +322,7 @@ static void __init setup_unwind_table(struct unwind_table *table, | |||
303 | ptr = (const u8 *)(fde + 2); | 322 | ptr = (const u8 *)(fde + 2); |
304 | header->table[n].start = read_pointer(&ptr, | 323 | header->table[n].start = read_pointer(&ptr, |
305 | (const u8 *)(fde + 1) + *fde, | 324 | (const u8 *)(fde + 1) + *fde, |
306 | fde_pointer_type(cie)); | 325 | fde_pointer_type(cie), 0, 0); |
307 | header->table[n].fde = (unsigned long)fde; | 326 | header->table[n].fde = (unsigned long)fde; |
308 | ++n; | 327 | ++n; |
309 | } | 328 | } |
@@ -486,7 +505,9 @@ static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) | |||
486 | 505 | ||
487 | static unsigned long read_pointer(const u8 **pLoc, | 506 | static unsigned long read_pointer(const u8 **pLoc, |
488 | const void *end, | 507 | const void *end, |
489 | signed ptrType) | 508 | signed ptrType, |
509 | unsigned long text_base, | ||
510 | unsigned long data_base) | ||
490 | { | 511 | { |
491 | unsigned long value = 0; | 512 | unsigned long value = 0; |
492 | union { | 513 | union { |
@@ -498,13 +519,17 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
498 | const unsigned long *pul; | 519 | const unsigned long *pul; |
499 | } ptr; | 520 | } ptr; |
500 | 521 | ||
501 | if (ptrType < 0 || ptrType == DW_EH_PE_omit) | 522 | if (ptrType < 0 || ptrType == DW_EH_PE_omit) { |
523 | dprintk(1, "Invalid pointer encoding %02X (%p,%p).", ptrType, *pLoc, end); | ||
502 | return 0; | 524 | return 0; |
525 | } | ||
503 | ptr.p8 = *pLoc; | 526 | ptr.p8 = *pLoc; |
504 | switch(ptrType & DW_EH_PE_FORM) { | 527 | switch(ptrType & DW_EH_PE_FORM) { |
505 | case DW_EH_PE_data2: | 528 | case DW_EH_PE_data2: |
506 | if (end < (const void *)(ptr.p16u + 1)) | 529 | if (end < (const void *)(ptr.p16u + 1)) { |
530 | dprintk(1, "Data16 overrun (%p,%p).", ptr.p8, end); | ||
507 | return 0; | 531 | return 0; |
532 | } | ||
508 | if(ptrType & DW_EH_PE_signed) | 533 | if(ptrType & DW_EH_PE_signed) |
509 | value = get_unaligned(ptr.p16s++); | 534 | value = get_unaligned(ptr.p16s++); |
510 | else | 535 | else |
@@ -512,8 +537,10 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
512 | break; | 537 | break; |
513 | case DW_EH_PE_data4: | 538 | case DW_EH_PE_data4: |
514 | #ifdef CONFIG_64BIT | 539 | #ifdef CONFIG_64BIT |
515 | if (end < (const void *)(ptr.p32u + 1)) | 540 | if (end < (const void *)(ptr.p32u + 1)) { |
541 | dprintk(1, "Data32 overrun (%p,%p).", ptr.p8, end); | ||
516 | return 0; | 542 | return 0; |
543 | } | ||
517 | if(ptrType & DW_EH_PE_signed) | 544 | if(ptrType & DW_EH_PE_signed) |
518 | value = get_unaligned(ptr.p32s++); | 545 | value = get_unaligned(ptr.p32s++); |
519 | else | 546 | else |
@@ -525,8 +552,10 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
525 | BUILD_BUG_ON(sizeof(u32) != sizeof(value)); | 552 | BUILD_BUG_ON(sizeof(u32) != sizeof(value)); |
526 | #endif | 553 | #endif |
527 | case DW_EH_PE_native: | 554 | case DW_EH_PE_native: |
528 | if (end < (const void *)(ptr.pul + 1)) | 555 | if (end < (const void *)(ptr.pul + 1)) { |
556 | dprintk(1, "DataUL overrun (%p,%p).", ptr.p8, end); | ||
529 | return 0; | 557 | return 0; |
558 | } | ||
530 | value = get_unaligned(ptr.pul++); | 559 | value = get_unaligned(ptr.pul++); |
531 | break; | 560 | break; |
532 | case DW_EH_PE_leb128: | 561 | case DW_EH_PE_leb128: |
@@ -534,10 +563,14 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
534 | value = ptrType & DW_EH_PE_signed | 563 | value = ptrType & DW_EH_PE_signed |
535 | ? get_sleb128(&ptr.p8, end) | 564 | ? get_sleb128(&ptr.p8, end) |
536 | : get_uleb128(&ptr.p8, end); | 565 | : get_uleb128(&ptr.p8, end); |
537 | if ((const void *)ptr.p8 > end) | 566 | if ((const void *)ptr.p8 > end) { |
567 | dprintk(1, "DataLEB overrun (%p,%p).", ptr.p8, end); | ||
538 | return 0; | 568 | return 0; |
569 | } | ||
539 | break; | 570 | break; |
540 | default: | 571 | default: |
572 | dprintk(2, "Cannot decode pointer type %02X (%p,%p).", | ||
573 | ptrType, ptr.p8, end); | ||
541 | return 0; | 574 | return 0; |
542 | } | 575 | } |
543 | switch(ptrType & DW_EH_PE_ADJUST) { | 576 | switch(ptrType & DW_EH_PE_ADJUST) { |
@@ -546,12 +579,33 @@ static unsigned long read_pointer(const u8 **pLoc, | |||
546 | case DW_EH_PE_pcrel: | 579 | case DW_EH_PE_pcrel: |
547 | value += (unsigned long)*pLoc; | 580 | value += (unsigned long)*pLoc; |
548 | break; | 581 | break; |
582 | case DW_EH_PE_textrel: | ||
583 | if (likely(text_base)) { | ||
584 | value += text_base; | ||
585 | break; | ||
586 | } | ||
587 | dprintk(2, "Text-relative encoding %02X (%p,%p), but zero text base.", | ||
588 | ptrType, *pLoc, end); | ||
589 | return 0; | ||
590 | case DW_EH_PE_datarel: | ||
591 | if (likely(data_base)) { | ||
592 | value += data_base; | ||
593 | break; | ||
594 | } | ||
595 | dprintk(2, "Data-relative encoding %02X (%p,%p), but zero data base.", | ||
596 | ptrType, *pLoc, end); | ||
597 | return 0; | ||
549 | default: | 598 | default: |
599 | dprintk(2, "Cannot adjust pointer type %02X (%p,%p).", | ||
600 | ptrType, *pLoc, end); | ||
550 | return 0; | 601 | return 0; |
551 | } | 602 | } |
552 | if ((ptrType & DW_EH_PE_indirect) | 603 | if ((ptrType & DW_EH_PE_indirect) |
553 | && __get_user(value, (unsigned long *)value)) | 604 | && probe_kernel_address((unsigned long *)value, value)) { |
605 | dprintk(1, "Cannot read indirect value %lx (%p,%p).", | ||
606 | value, *pLoc, end); | ||
554 | return 0; | 607 | return 0; |
608 | } | ||
555 | *pLoc = ptr.p8; | 609 | *pLoc = ptr.p8; |
556 | 610 | ||
557 | return value; | 611 | return value; |
@@ -594,7 +648,8 @@ static signed fde_pointer_type(const u32 *cie) | |||
594 | case 'P': { | 648 | case 'P': { |
595 | signed ptrType = *ptr++; | 649 | signed ptrType = *ptr++; |
596 | 650 | ||
597 | if (!read_pointer(&ptr, end, ptrType) || ptr > end) | 651 | if (!read_pointer(&ptr, end, ptrType, 0, 0) |
652 | || ptr > end) | ||
598 | return -1; | 653 | return -1; |
599 | } | 654 | } |
600 | break; | 655 | break; |
@@ -654,7 +709,8 @@ static int processCFI(const u8 *start, | |||
654 | case DW_CFA_nop: | 709 | case DW_CFA_nop: |
655 | break; | 710 | break; |
656 | case DW_CFA_set_loc: | 711 | case DW_CFA_set_loc: |
657 | if ((state->loc = read_pointer(&ptr.p8, end, ptrType)) == 0) | 712 | state->loc = read_pointer(&ptr.p8, end, ptrType, 0, 0); |
713 | if (state->loc == 0) | ||
658 | result = 0; | 714 | result = 0; |
659 | break; | 715 | break; |
660 | case DW_CFA_advance_loc1: | 716 | case DW_CFA_advance_loc1: |
@@ -700,8 +756,10 @@ static int processCFI(const u8 *start, | |||
700 | state->label = NULL; | 756 | state->label = NULL; |
701 | return 1; | 757 | return 1; |
702 | } | 758 | } |
703 | if (state->stackDepth >= MAX_STACK_DEPTH) | 759 | if (state->stackDepth >= MAX_STACK_DEPTH) { |
760 | dprintk(1, "State stack overflow (%p,%p).", ptr.p8, end); | ||
704 | return 0; | 761 | return 0; |
762 | } | ||
705 | state->stack[state->stackDepth++] = ptr.p8; | 763 | state->stack[state->stackDepth++] = ptr.p8; |
706 | break; | 764 | break; |
707 | case DW_CFA_restore_state: | 765 | case DW_CFA_restore_state: |
@@ -716,8 +774,10 @@ static int processCFI(const u8 *start, | |||
716 | result = processCFI(start, end, 0, ptrType, state); | 774 | result = processCFI(start, end, 0, ptrType, state); |
717 | state->loc = loc; | 775 | state->loc = loc; |
718 | state->label = label; | 776 | state->label = label; |
719 | } else | 777 | } else { |
778 | dprintk(1, "State stack underflow (%p,%p).", ptr.p8, end); | ||
720 | return 0; | 779 | return 0; |
780 | } | ||
721 | break; | 781 | break; |
722 | case DW_CFA_def_cfa: | 782 | case DW_CFA_def_cfa: |
723 | state->cfa.reg = get_uleb128(&ptr.p8, end); | 783 | state->cfa.reg = get_uleb128(&ptr.p8, end); |
@@ -749,6 +809,7 @@ static int processCFI(const u8 *start, | |||
749 | break; | 809 | break; |
750 | case DW_CFA_GNU_window_save: | 810 | case DW_CFA_GNU_window_save: |
751 | default: | 811 | default: |
812 | dprintk(1, "Unrecognized CFI op %02X (%p,%p).", ptr.p8[-1], ptr.p8 - 1, end); | ||
752 | result = 0; | 813 | result = 0; |
753 | break; | 814 | break; |
754 | } | 815 | } |
@@ -764,12 +825,17 @@ static int processCFI(const u8 *start, | |||
764 | set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); | 825 | set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); |
765 | break; | 826 | break; |
766 | } | 827 | } |
767 | if (ptr.p8 > end) | 828 | if (ptr.p8 > end) { |
829 | dprintk(1, "Data overrun (%p,%p).", ptr.p8, end); | ||
768 | result = 0; | 830 | result = 0; |
831 | } | ||
769 | if (result && targetLoc != 0 && targetLoc < state->loc) | 832 | if (result && targetLoc != 0 && targetLoc < state->loc) |
770 | return 1; | 833 | return 1; |
771 | } | 834 | } |
772 | 835 | ||
836 | if (result && ptr.p8 < end) | ||
837 | dprintk(1, "Data underrun (%p,%p).", ptr.p8, end); | ||
838 | |||
773 | return result | 839 | return result |
774 | && ptr.p8 == end | 840 | && ptr.p8 == end |
775 | && (targetLoc == 0 | 841 | && (targetLoc == 0 |
@@ -786,7 +852,7 @@ int unwind(struct unwind_frame_info *frame) | |||
786 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) | 852 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) |
787 | const u32 *fde = NULL, *cie = NULL; | 853 | const u32 *fde = NULL, *cie = NULL; |
788 | const u8 *ptr = NULL, *end = NULL; | 854 | const u8 *ptr = NULL, *end = NULL; |
789 | unsigned long pc = UNW_PC(frame) - frame->call_frame; | 855 | unsigned long pc = UNW_PC(frame) - frame->call_frame, sp; |
790 | unsigned long startLoc = 0, endLoc = 0, cfa; | 856 | unsigned long startLoc = 0, endLoc = 0, cfa; |
791 | unsigned i; | 857 | unsigned i; |
792 | signed ptrType = -1; | 858 | signed ptrType = -1; |
@@ -813,9 +879,9 @@ int unwind(struct unwind_frame_info *frame) | |||
813 | ptr = hdr + 4; | 879 | ptr = hdr + 4; |
814 | end = hdr + table->hdrsz; | 880 | end = hdr + table->hdrsz; |
815 | if (tableSize | 881 | if (tableSize |
816 | && read_pointer(&ptr, end, hdr[1]) | 882 | && read_pointer(&ptr, end, hdr[1], 0, 0) |
817 | == (unsigned long)table->address | 883 | == (unsigned long)table->address |
818 | && (i = read_pointer(&ptr, end, hdr[2])) > 0 | 884 | && (i = read_pointer(&ptr, end, hdr[2], 0, 0)) > 0 |
819 | && i == (end - ptr) / (2 * tableSize) | 885 | && i == (end - ptr) / (2 * tableSize) |
820 | && !((end - ptr) % (2 * tableSize))) { | 886 | && !((end - ptr) % (2 * tableSize))) { |
821 | do { | 887 | do { |
@@ -823,7 +889,8 @@ int unwind(struct unwind_frame_info *frame) | |||
823 | 889 | ||
824 | startLoc = read_pointer(&cur, | 890 | startLoc = read_pointer(&cur, |
825 | cur + tableSize, | 891 | cur + tableSize, |
826 | hdr[3]); | 892 | hdr[3], 0, |
893 | (unsigned long)hdr); | ||
827 | if (pc < startLoc) | 894 | if (pc < startLoc) |
828 | i /= 2; | 895 | i /= 2; |
829 | else { | 896 | else { |
@@ -834,13 +901,17 @@ int unwind(struct unwind_frame_info *frame) | |||
834 | if (i == 1 | 901 | if (i == 1 |
835 | && (startLoc = read_pointer(&ptr, | 902 | && (startLoc = read_pointer(&ptr, |
836 | ptr + tableSize, | 903 | ptr + tableSize, |
837 | hdr[3])) != 0 | 904 | hdr[3], 0, |
905 | (unsigned long)hdr)) != 0 | ||
838 | && pc >= startLoc) | 906 | && pc >= startLoc) |
839 | fde = (void *)read_pointer(&ptr, | 907 | fde = (void *)read_pointer(&ptr, |
840 | ptr + tableSize, | 908 | ptr + tableSize, |
841 | hdr[3]); | 909 | hdr[3], 0, |
910 | (unsigned long)hdr); | ||
842 | } | 911 | } |
843 | } | 912 | } |
913 | if(hdr && !fde) | ||
914 | dprintk(3, "Binary lookup for %lx failed.", pc); | ||
844 | 915 | ||
845 | if (fde != NULL) { | 916 | if (fde != NULL) { |
846 | cie = cie_for_fde(fde, table); | 917 | cie = cie_for_fde(fde, table); |
@@ -851,17 +922,19 @@ int unwind(struct unwind_frame_info *frame) | |||
851 | && (ptrType = fde_pointer_type(cie)) >= 0 | 922 | && (ptrType = fde_pointer_type(cie)) >= 0 |
852 | && read_pointer(&ptr, | 923 | && read_pointer(&ptr, |
853 | (const u8 *)(fde + 1) + *fde, | 924 | (const u8 *)(fde + 1) + *fde, |
854 | ptrType) == startLoc) { | 925 | ptrType, 0, 0) == startLoc) { |
855 | if (!(ptrType & DW_EH_PE_indirect)) | 926 | if (!(ptrType & DW_EH_PE_indirect)) |
856 | ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; | 927 | ptrType &= DW_EH_PE_FORM|DW_EH_PE_signed; |
857 | endLoc = startLoc | 928 | endLoc = startLoc |
858 | + read_pointer(&ptr, | 929 | + read_pointer(&ptr, |
859 | (const u8 *)(fde + 1) + *fde, | 930 | (const u8 *)(fde + 1) + *fde, |
860 | ptrType); | 931 | ptrType, 0, 0); |
861 | if(pc >= endLoc) | 932 | if(pc >= endLoc) |
862 | fde = NULL; | 933 | fde = NULL; |
863 | } else | 934 | } else |
864 | fde = NULL; | 935 | fde = NULL; |
936 | if(!fde) | ||
937 | dprintk(1, "Binary lookup result for %lx discarded.", pc); | ||
865 | } | 938 | } |
866 | if (fde == NULL) { | 939 | if (fde == NULL) { |
867 | for (fde = table->address, tableSize = table->size; | 940 | for (fde = table->address, tableSize = table->size; |
@@ -881,7 +954,7 @@ int unwind(struct unwind_frame_info *frame) | |||
881 | ptr = (const u8 *)(fde + 2); | 954 | ptr = (const u8 *)(fde + 2); |
882 | startLoc = read_pointer(&ptr, | 955 | startLoc = read_pointer(&ptr, |
883 | (const u8 *)(fde + 1) + *fde, | 956 | (const u8 *)(fde + 1) + *fde, |
884 | ptrType); | 957 | ptrType, 0, 0); |
885 | if (!startLoc) | 958 | if (!startLoc) |
886 | continue; | 959 | continue; |
887 | if (!(ptrType & DW_EH_PE_indirect)) | 960 | if (!(ptrType & DW_EH_PE_indirect)) |
@@ -889,10 +962,12 @@ int unwind(struct unwind_frame_info *frame) | |||
889 | endLoc = startLoc | 962 | endLoc = startLoc |
890 | + read_pointer(&ptr, | 963 | + read_pointer(&ptr, |
891 | (const u8 *)(fde + 1) + *fde, | 964 | (const u8 *)(fde + 1) + *fde, |
892 | ptrType); | 965 | ptrType, 0, 0); |
893 | if (pc >= startLoc && pc < endLoc) | 966 | if (pc >= startLoc && pc < endLoc) |
894 | break; | 967 | break; |
895 | } | 968 | } |
969 | if(!fde) | ||
970 | dprintk(3, "Linear lookup for %lx failed.", pc); | ||
896 | } | 971 | } |
897 | } | 972 | } |
898 | if (cie != NULL) { | 973 | if (cie != NULL) { |
@@ -926,6 +1001,8 @@ int unwind(struct unwind_frame_info *frame) | |||
926 | if (ptr >= end || *ptr) | 1001 | if (ptr >= end || *ptr) |
927 | cie = NULL; | 1002 | cie = NULL; |
928 | } | 1003 | } |
1004 | if(!cie) | ||
1005 | dprintk(1, "CIE unusable (%p,%p).", ptr, end); | ||
929 | ++ptr; | 1006 | ++ptr; |
930 | } | 1007 | } |
931 | if (cie != NULL) { | 1008 | if (cie != NULL) { |
@@ -935,7 +1012,12 @@ int unwind(struct unwind_frame_info *frame) | |||
935 | state.dataAlign = get_sleb128(&ptr, end); | 1012 | state.dataAlign = get_sleb128(&ptr, end); |
936 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) | 1013 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) |
937 | cie = NULL; | 1014 | cie = NULL; |
938 | else { | 1015 | else if (UNW_PC(frame) % state.codeAlign |
1016 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) { | ||
1017 | dprintk(1, "Input pointer(s) misaligned (%lx,%lx).", | ||
1018 | UNW_PC(frame), UNW_SP(frame)); | ||
1019 | return -EPERM; | ||
1020 | } else { | ||
939 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); | 1021 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); |
940 | /* skip augmentation */ | 1022 | /* skip augmentation */ |
941 | if (((const char *)(cie + 2))[1] == 'z') { | 1023 | if (((const char *)(cie + 2))[1] == 'z') { |
@@ -949,6 +1031,8 @@ int unwind(struct unwind_frame_info *frame) | |||
949 | || reg_info[retAddrReg].width != sizeof(unsigned long)) | 1031 | || reg_info[retAddrReg].width != sizeof(unsigned long)) |
950 | cie = NULL; | 1032 | cie = NULL; |
951 | } | 1033 | } |
1034 | if(!cie) | ||
1035 | dprintk(1, "CIE validation failed (%p,%p).", ptr, end); | ||
952 | } | 1036 | } |
953 | if (cie != NULL) { | 1037 | if (cie != NULL) { |
954 | state.cieStart = ptr; | 1038 | state.cieStart = ptr; |
@@ -962,11 +1046,15 @@ int unwind(struct unwind_frame_info *frame) | |||
962 | if ((ptr += augSize) > end) | 1046 | if ((ptr += augSize) > end) |
963 | fde = NULL; | 1047 | fde = NULL; |
964 | } | 1048 | } |
1049 | if(!fde) | ||
1050 | dprintk(1, "FDE validation failed (%p,%p).", ptr, end); | ||
965 | } | 1051 | } |
966 | if (cie == NULL || fde == NULL) { | 1052 | if (cie == NULL || fde == NULL) { |
967 | #ifdef CONFIG_FRAME_POINTER | 1053 | #ifdef CONFIG_FRAME_POINTER |
968 | unsigned long top, bottom; | 1054 | unsigned long top, bottom; |
969 | 1055 | ||
1056 | if ((UNW_SP(frame) | UNW_FP(frame)) % sizeof(unsigned long)) | ||
1057 | return -EPERM; | ||
970 | top = STACK_TOP(frame->task); | 1058 | top = STACK_TOP(frame->task); |
971 | bottom = STACK_BOTTOM(frame->task); | 1059 | bottom = STACK_BOTTOM(frame->task); |
972 | # if FRAME_RETADDR_OFFSET < 0 | 1060 | # if FRAME_RETADDR_OFFSET < 0 |
@@ -982,18 +1070,19 @@ int unwind(struct unwind_frame_info *frame) | |||
982 | & (sizeof(unsigned long) - 1))) { | 1070 | & (sizeof(unsigned long) - 1))) { |
983 | unsigned long link; | 1071 | unsigned long link; |
984 | 1072 | ||
985 | if (!__get_user(link, | 1073 | if (!probe_kernel_address( |
986 | (unsigned long *)(UNW_FP(frame) | 1074 | (unsigned long *)(UNW_FP(frame) |
987 | + FRAME_LINK_OFFSET)) | 1075 | + FRAME_LINK_OFFSET), |
1076 | link) | ||
988 | # if FRAME_RETADDR_OFFSET < 0 | 1077 | # if FRAME_RETADDR_OFFSET < 0 |
989 | && link > bottom && link < UNW_FP(frame) | 1078 | && link > bottom && link < UNW_FP(frame) |
990 | # else | 1079 | # else |
991 | && link > UNW_FP(frame) && link < bottom | 1080 | && link > UNW_FP(frame) && link < bottom |
992 | # endif | 1081 | # endif |
993 | && !(link & (sizeof(link) - 1)) | 1082 | && !(link & (sizeof(link) - 1)) |
994 | && !__get_user(UNW_PC(frame), | 1083 | && !probe_kernel_address( |
995 | (unsigned long *)(UNW_FP(frame) | 1084 | (unsigned long *)(UNW_FP(frame) |
996 | + FRAME_RETADDR_OFFSET))) { | 1085 | + FRAME_RETADDR_OFFSET), UNW_PC(frame))) { |
997 | UNW_SP(frame) = UNW_FP(frame) + FRAME_RETADDR_OFFSET | 1086 | UNW_SP(frame) = UNW_FP(frame) + FRAME_RETADDR_OFFSET |
998 | # if FRAME_RETADDR_OFFSET < 0 | 1087 | # if FRAME_RETADDR_OFFSET < 0 |
999 | - | 1088 | - |
@@ -1016,8 +1105,11 @@ int unwind(struct unwind_frame_info *frame) | |||
1016 | || state.regs[retAddrReg].where == Nowhere | 1105 | || state.regs[retAddrReg].where == Nowhere |
1017 | || state.cfa.reg >= ARRAY_SIZE(reg_info) | 1106 | || state.cfa.reg >= ARRAY_SIZE(reg_info) |
1018 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) | 1107 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) |
1019 | || state.cfa.offs % sizeof(unsigned long)) | 1108 | || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) |
1109 | || state.cfa.offs % sizeof(unsigned long)) { | ||
1110 | dprintk(1, "Unusable unwind info (%p,%p).", ptr, end); | ||
1020 | return -EIO; | 1111 | return -EIO; |
1112 | } | ||
1021 | /* update frame */ | 1113 | /* update frame */ |
1022 | #ifndef CONFIG_AS_CFI_SIGNAL_FRAME | 1114 | #ifndef CONFIG_AS_CFI_SIGNAL_FRAME |
1023 | if(frame->call_frame | 1115 | if(frame->call_frame |
@@ -1036,10 +1128,14 @@ int unwind(struct unwind_frame_info *frame) | |||
1036 | #else | 1128 | #else |
1037 | # define CASES CASE(8); CASE(16); CASE(32); CASE(64) | 1129 | # define CASES CASE(8); CASE(16); CASE(32); CASE(64) |
1038 | #endif | 1130 | #endif |
1131 | pc = UNW_PC(frame); | ||
1132 | sp = UNW_SP(frame); | ||
1039 | for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { | 1133 | for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { |
1040 | if (REG_INVALID(i)) { | 1134 | if (REG_INVALID(i)) { |
1041 | if (state.regs[i].where == Nowhere) | 1135 | if (state.regs[i].where == Nowhere) |
1042 | continue; | 1136 | continue; |
1137 | dprintk(1, "Cannot restore register %u (%d).", | ||
1138 | i, state.regs[i].where); | ||
1043 | return -EIO; | 1139 | return -EIO; |
1044 | } | 1140 | } |
1045 | switch(state.regs[i].where) { | 1141 | switch(state.regs[i].where) { |
@@ -1048,8 +1144,11 @@ int unwind(struct unwind_frame_info *frame) | |||
1048 | case Register: | 1144 | case Register: |
1049 | if (state.regs[i].value >= ARRAY_SIZE(reg_info) | 1145 | if (state.regs[i].value >= ARRAY_SIZE(reg_info) |
1050 | || REG_INVALID(state.regs[i].value) | 1146 | || REG_INVALID(state.regs[i].value) |
1051 | || reg_info[i].width > reg_info[state.regs[i].value].width) | 1147 | || reg_info[i].width > reg_info[state.regs[i].value].width) { |
1148 | dprintk(1, "Cannot restore register %u from register %lu.", | ||
1149 | i, state.regs[i].value); | ||
1052 | return -EIO; | 1150 | return -EIO; |
1151 | } | ||
1053 | switch(reg_info[state.regs[i].value].width) { | 1152 | switch(reg_info[state.regs[i].value].width) { |
1054 | #define CASE(n) \ | 1153 | #define CASE(n) \ |
1055 | case sizeof(u##n): \ | 1154 | case sizeof(u##n): \ |
@@ -1059,6 +1158,9 @@ int unwind(struct unwind_frame_info *frame) | |||
1059 | CASES; | 1158 | CASES; |
1060 | #undef CASE | 1159 | #undef CASE |
1061 | default: | 1160 | default: |
1161 | dprintk(1, "Unsupported register size %u (%lu).", | ||
1162 | reg_info[state.regs[i].value].width, | ||
1163 | state.regs[i].value); | ||
1062 | return -EIO; | 1164 | return -EIO; |
1063 | } | 1165 | } |
1064 | break; | 1166 | break; |
@@ -1083,12 +1185,17 @@ int unwind(struct unwind_frame_info *frame) | |||
1083 | CASES; | 1185 | CASES; |
1084 | #undef CASE | 1186 | #undef CASE |
1085 | default: | 1187 | default: |
1188 | dprintk(1, "Unsupported register size %u (%u).", | ||
1189 | reg_info[i].width, i); | ||
1086 | return -EIO; | 1190 | return -EIO; |
1087 | } | 1191 | } |
1088 | break; | 1192 | break; |
1089 | case Value: | 1193 | case Value: |
1090 | if (reg_info[i].width != sizeof(unsigned long)) | 1194 | if (reg_info[i].width != sizeof(unsigned long)) { |
1195 | dprintk(1, "Unsupported value size %u (%u).", | ||
1196 | reg_info[i].width, i); | ||
1091 | return -EIO; | 1197 | return -EIO; |
1198 | } | ||
1092 | FRAME_REG(i, unsigned long) = cfa + state.regs[i].value | 1199 | FRAME_REG(i, unsigned long) = cfa + state.regs[i].value |
1093 | * state.dataAlign; | 1200 | * state.dataAlign; |
1094 | break; | 1201 | break; |
@@ -1100,15 +1207,20 @@ int unwind(struct unwind_frame_info *frame) | |||
1100 | % sizeof(unsigned long) | 1207 | % sizeof(unsigned long) |
1101 | || addr < startLoc | 1208 | || addr < startLoc |
1102 | || addr + sizeof(unsigned long) < addr | 1209 | || addr + sizeof(unsigned long) < addr |
1103 | || addr + sizeof(unsigned long) > endLoc) | 1210 | || addr + sizeof(unsigned long) > endLoc) { |
1211 | dprintk(1, "Bad memory location %lx (%lx).", | ||
1212 | addr, state.regs[i].value); | ||
1104 | return -EIO; | 1213 | return -EIO; |
1214 | } | ||
1105 | switch(reg_info[i].width) { | 1215 | switch(reg_info[i].width) { |
1106 | #define CASE(n) case sizeof(u##n): \ | 1216 | #define CASE(n) case sizeof(u##n): \ |
1107 | __get_user(FRAME_REG(i, u##n), (u##n *)addr); \ | 1217 | probe_kernel_address((u##n *)addr, FRAME_REG(i, u##n)); \ |
1108 | break | 1218 | break |
1109 | CASES; | 1219 | CASES; |
1110 | #undef CASE | 1220 | #undef CASE |
1111 | default: | 1221 | default: |
1222 | dprintk(1, "Unsupported memory size %u (%u).", | ||
1223 | reg_info[i].width, i); | ||
1112 | return -EIO; | 1224 | return -EIO; |
1113 | } | 1225 | } |
1114 | } | 1226 | } |
@@ -1116,6 +1228,17 @@ int unwind(struct unwind_frame_info *frame) | |||
1116 | } | 1228 | } |
1117 | } | 1229 | } |
1118 | 1230 | ||
1231 | if (UNW_PC(frame) % state.codeAlign | ||
1232 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) { | ||
1233 | dprintk(1, "Output pointer(s) misaligned (%lx,%lx).", | ||
1234 | UNW_PC(frame), UNW_SP(frame)); | ||
1235 | return -EIO; | ||
1236 | } | ||
1237 | if (pc == UNW_PC(frame) && sp == UNW_SP(frame)) { | ||
1238 | dprintk(1, "No progress (%lx,%lx).", pc, sp); | ||
1239 | return -EIO; | ||
1240 | } | ||
1241 | |||
1119 | return 0; | 1242 | return 0; |
1120 | #undef CASES | 1243 | #undef CASES |
1121 | #undef FRAME_REG | 1244 | #undef FRAME_REG |