aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@novell.com>2006-12-06 20:14:13 -0500
committerAndi Kleen <andi@basil.nowhere.org>2006-12-06 20:14:13 -0500
commit359ad0d4015a9ab39243f2ebc4eb07915bd618b2 (patch)
tree90f05d8d9ab048029bfe1e451a012b4d5896aafe
parenteef5e0d185fc049bda11fa14ba286fbd357da896 (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.c7
-rw-r--r--arch/x86_64/kernel/traps.c7
-rw-r--r--include/asm-i386/unwind.h12
-rw-r--r--include/asm-x86_64/unwind.h8
-rw-r--r--kernel/unwind.c16
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
82static inline int arch_unw_user_mode(const struct unwind_frame_info *info) 82static 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
88static inline int arch_unw_user_mode(const struct unwind_frame_info *info) 88static 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
96typedef unsigned long uleb128_t; 96typedef unsigned long uleb128_t;
97typedef signed long sleb128_t; 97typedef signed long sleb128_t;
98#define sleb128abs __builtin_labs
98 99
99static struct unwind_table { 100static 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