diff options
author | Jan Beulich <jbeulich@novell.com> | 2006-12-06 20:14:13 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:13 -0500 |
commit | 359ad0d4015a9ab39243f2ebc4eb07915bd618b2 (patch) | |
tree | 90f05d8d9ab048029bfe1e451a012b4d5896aafe | |
parent | eef5e0d185fc049bda11fa14ba286fbd357da896 (diff) |
[PATCH] unwinder: more sanity checks in Dwarf2 unwinder
Tighten the requirements on both input to and output from the Dwarf2
unwinder.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
-rw-r--r-- | arch/i386/kernel/traps.c | 7 | ||||
-rw-r--r-- | arch/x86_64/kernel/traps.c | 7 | ||||
-rw-r--r-- | include/asm-i386/unwind.h | 12 | ||||
-rw-r--r-- | include/asm-x86_64/unwind.h | 8 | ||||
-rw-r--r-- | kernel/unwind.c | 16 |
5 files changed, 35 insertions, 15 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 86d8476be4fe..c447807e2a45 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c | |||
@@ -161,12 +161,19 @@ dump_trace_unwind(struct unwind_frame_info *info, void *data) | |||
161 | { | 161 | { |
162 | struct ops_and_data *oad = (struct ops_and_data *)data; | 162 | struct ops_and_data *oad = (struct ops_and_data *)data; |
163 | int n = 0; | 163 | int n = 0; |
164 | unsigned long sp = UNW_SP(info); | ||
164 | 165 | ||
166 | if (arch_unw_user_mode(info)) | ||
167 | return -1; | ||
165 | while (unwind(info) == 0 && UNW_PC(info)) { | 168 | while (unwind(info) == 0 && UNW_PC(info)) { |
166 | n++; | 169 | n++; |
167 | oad->ops->address(oad->data, UNW_PC(info)); | 170 | oad->ops->address(oad->data, UNW_PC(info)); |
168 | if (arch_unw_user_mode(info)) | 171 | if (arch_unw_user_mode(info)) |
169 | break; | 172 | break; |
173 | if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) | ||
174 | && sp > UNW_SP(info)) | ||
175 | break; | ||
176 | sp = UNW_SP(info); | ||
170 | } | 177 | } |
171 | return n; | 178 | return n; |
172 | } | 179 | } |
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 9864d195c408..4fdd162f0bef 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -225,12 +225,19 @@ static int dump_trace_unwind(struct unwind_frame_info *info, void *context) | |||
225 | { | 225 | { |
226 | struct ops_and_data *oad = (struct ops_and_data *)context; | 226 | struct ops_and_data *oad = (struct ops_and_data *)context; |
227 | int n = 0; | 227 | int n = 0; |
228 | unsigned long sp = UNW_SP(info); | ||
228 | 229 | ||
230 | if (arch_unw_user_mode(info)) | ||
231 | return -1; | ||
229 | while (unwind(info) == 0 && UNW_PC(info)) { | 232 | while (unwind(info) == 0 && UNW_PC(info)) { |
230 | n++; | 233 | n++; |
231 | oad->ops->address(oad->data, UNW_PC(info)); | 234 | oad->ops->address(oad->data, UNW_PC(info)); |
232 | if (arch_unw_user_mode(info)) | 235 | if (arch_unw_user_mode(info)) |
233 | break; | 236 | break; |
237 | if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) | ||
238 | && sp > UNW_SP(info)) | ||
239 | break; | ||
240 | sp = UNW_SP(info); | ||
234 | } | 241 | } |
235 | return n; | 242 | return n; |
236 | } | 243 | } |
diff --git a/include/asm-i386/unwind.h b/include/asm-i386/unwind.h index 601fc67bd775..aa2c931e30db 100644 --- a/include/asm-i386/unwind.h +++ b/include/asm-i386/unwind.h | |||
@@ -79,17 +79,13 @@ extern asmlinkage int arch_unwind_init_running(struct unwind_frame_info *, | |||
79 | void *arg), | 79 | void *arg), |
80 | void *arg); | 80 | void *arg); |
81 | 81 | ||
82 | static inline int arch_unw_user_mode(const struct unwind_frame_info *info) | 82 | static inline int arch_unw_user_mode(/*const*/ struct unwind_frame_info *info) |
83 | { | 83 | { |
84 | #if 0 /* This can only work when selector register and EFLAGS saves/restores | 84 | return user_mode_vm(&info->regs) |
85 | are properly annotated (and tracked in UNW_REGISTER_INFO). */ | 85 | || info->regs.eip < PAGE_OFFSET |
86 | return user_mode_vm(&info->regs); | ||
87 | #else | ||
88 | return info->regs.eip < PAGE_OFFSET | ||
89 | || (info->regs.eip >= __fix_to_virt(FIX_VDSO) | 86 | || (info->regs.eip >= __fix_to_virt(FIX_VDSO) |
90 | && info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) | 87 | && info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) |
91 | || info->regs.esp < PAGE_OFFSET; | 88 | || info->regs.esp < PAGE_OFFSET; |
92 | #endif | ||
93 | } | 89 | } |
94 | 90 | ||
95 | #else | 91 | #else |
diff --git a/include/asm-x86_64/unwind.h b/include/asm-x86_64/unwind.h index 2e7ff10fd775..2f6349e48717 100644 --- a/include/asm-x86_64/unwind.h +++ b/include/asm-x86_64/unwind.h | |||
@@ -87,14 +87,10 @@ extern int arch_unwind_init_running(struct unwind_frame_info *, | |||
87 | 87 | ||
88 | static inline int arch_unw_user_mode(const struct unwind_frame_info *info) | 88 | static inline int arch_unw_user_mode(const struct unwind_frame_info *info) |
89 | { | 89 | { |
90 | #if 0 /* This can only work when selector register saves/restores | 90 | return user_mode(&info->regs) |
91 | are properly annotated (and tracked in UNW_REGISTER_INFO). */ | 91 | || (long)info->regs.rip >= 0 |
92 | return user_mode(&info->regs); | ||
93 | #else | ||
94 | return (long)info->regs.rip >= 0 | ||
95 | || (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END) | 92 | || (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END) |
96 | || (long)info->regs.rsp >= 0; | 93 | || (long)info->regs.rsp >= 0; |
97 | #endif | ||
98 | } | 94 | } |
99 | 95 | ||
100 | #else | 96 | #else |
diff --git a/kernel/unwind.c b/kernel/unwind.c index af48168a3afb..7e721f104105 100644 --- a/kernel/unwind.c +++ b/kernel/unwind.c | |||
@@ -95,6 +95,7 @@ static const struct { | |||
95 | 95 | ||
96 | typedef unsigned long uleb128_t; | 96 | typedef unsigned long uleb128_t; |
97 | typedef signed long sleb128_t; | 97 | typedef signed long sleb128_t; |
98 | #define sleb128abs __builtin_labs | ||
98 | 99 | ||
99 | static struct unwind_table { | 100 | static struct unwind_table { |
100 | struct { | 101 | struct { |
@@ -787,7 +788,7 @@ int unwind(struct unwind_frame_info *frame) | |||
787 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) | 788 | #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) |
788 | const u32 *fde = NULL, *cie = NULL; | 789 | const u32 *fde = NULL, *cie = NULL; |
789 | const u8 *ptr = NULL, *end = NULL; | 790 | const u8 *ptr = NULL, *end = NULL; |
790 | unsigned long pc = UNW_PC(frame) - frame->call_frame; | 791 | unsigned long pc = UNW_PC(frame) - frame->call_frame, sp; |
791 | unsigned long startLoc = 0, endLoc = 0, cfa; | 792 | unsigned long startLoc = 0, endLoc = 0, cfa; |
792 | unsigned i; | 793 | unsigned i; |
793 | signed ptrType = -1; | 794 | signed ptrType = -1; |
@@ -936,6 +937,9 @@ int unwind(struct unwind_frame_info *frame) | |||
936 | state.dataAlign = get_sleb128(&ptr, end); | 937 | state.dataAlign = get_sleb128(&ptr, end); |
937 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) | 938 | if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) |
938 | cie = NULL; | 939 | cie = NULL; |
940 | else if (UNW_PC(frame) % state.codeAlign | ||
941 | || UNW_SP(frame) % sleb128abs(state.dataAlign)) | ||
942 | return -EPERM; | ||
939 | else { | 943 | else { |
940 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); | 944 | retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); |
941 | /* skip augmentation */ | 945 | /* skip augmentation */ |
@@ -968,6 +972,8 @@ int unwind(struct unwind_frame_info *frame) | |||
968 | #ifdef CONFIG_FRAME_POINTER | 972 | #ifdef CONFIG_FRAME_POINTER |
969 | unsigned long top, bottom; | 973 | unsigned long top, bottom; |
970 | 974 | ||
975 | if ((UNW_SP(frame) | UNW_FP(frame)) % sizeof(unsigned long)) | ||
976 | return -EPERM; | ||
971 | top = STACK_TOP(frame->task); | 977 | top = STACK_TOP(frame->task); |
972 | bottom = STACK_BOTTOM(frame->task); | 978 | bottom = STACK_BOTTOM(frame->task); |
973 | # if FRAME_RETADDR_OFFSET < 0 | 979 | # if FRAME_RETADDR_OFFSET < 0 |
@@ -1018,6 +1024,7 @@ int unwind(struct unwind_frame_info *frame) | |||
1018 | || state.regs[retAddrReg].where == Nowhere | 1024 | || state.regs[retAddrReg].where == Nowhere |
1019 | || state.cfa.reg >= ARRAY_SIZE(reg_info) | 1025 | || state.cfa.reg >= ARRAY_SIZE(reg_info) |
1020 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) | 1026 | || reg_info[state.cfa.reg].width != sizeof(unsigned long) |
1027 | || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) | ||
1021 | || state.cfa.offs % sizeof(unsigned long)) | 1028 | || state.cfa.offs % sizeof(unsigned long)) |
1022 | return -EIO; | 1029 | return -EIO; |
1023 | /* update frame */ | 1030 | /* update frame */ |
@@ -1038,6 +1045,8 @@ int unwind(struct unwind_frame_info *frame) | |||
1038 | #else | 1045 | #else |
1039 | # define CASES CASE(8); CASE(16); CASE(32); CASE(64) | 1046 | # define CASES CASE(8); CASE(16); CASE(32); CASE(64) |
1040 | #endif | 1047 | #endif |
1048 | pc = UNW_PC(frame); | ||
1049 | sp = UNW_SP(frame); | ||
1041 | for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { | 1050 | for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { |
1042 | if (REG_INVALID(i)) { | 1051 | if (REG_INVALID(i)) { |
1043 | if (state.regs[i].where == Nowhere) | 1052 | if (state.regs[i].where == Nowhere) |
@@ -1118,6 +1127,11 @@ int unwind(struct unwind_frame_info *frame) | |||
1118 | } | 1127 | } |
1119 | } | 1128 | } |
1120 | 1129 | ||
1130 | if (UNW_PC(frame) % state.codeAlign | ||
1131 | || UNW_SP(frame) % sleb128abs(state.dataAlign) | ||
1132 | || (pc == UNW_PC(frame) && sp == UNW_SP(frame))) | ||
1133 | return -EIO; | ||
1134 | |||
1121 | return 0; | 1135 | return 0; |
1122 | #undef CASES | 1136 | #undef CASES |
1123 | #undef FRAME_REG | 1137 | #undef FRAME_REG |