Skip to content

Commit a63343f

Browse files
committed
dtrace: ensure ustack works regardless of glibc stack implementation
Older glibc versions were providing start code to be linked into executable with a flawed implementation for setting up the initial frame. As a result, the frame usually ended up with a frame pointer value pointing to the beginning of the stack. Because this was the established implementation, the ustack() implemented for aarch64 was based on this design. Later glibc versions fix this mistake, and set up the initial frame with a NULL frame pointer (as appears to be expected on aarch64). The ustack() unwinder for aarch64 has been updated to handle both versions of initial frames correctly. Orabug: 29174561 Signed-off-by: Kris Van Hees <[email protected]> Reviewed-by: Nick Alcock <[email protected]>
1 parent 1ee069f commit a63343f

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

arch/arm64/kernel/dtrace_util.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,34 @@ static int dtrace_unwind_frame(struct user_stackframe *frame)
8787
return -EINVAL;
8888
}
8989

90-
/* Verify strictly increasing consecutive values. Since the stack
90+
/*
91+
* If the frame pointer in the current frame is NULL, we have reached
92+
* the end of the call chain.
93+
*/
94+
if (frame->fp == NULL)
95+
return 0;
96+
97+
/*
98+
* In older glibc versions, the call chain did not end with an initial
99+
* frame with NULL frame pointer. Instead, the initial frame stored
100+
* the beginning of the stack as frame pointer. We look for that here
101+
* as a special case, and return a frame where the frame pointer is
102+
* set to NULL (as it ought to be).
103+
*
104+
* If we do not know the beginning of the stack, we are out of luck.
105+
*/
106+
if (current->dt_task && current->dt_task->dt_ustack == frame->fp) {
107+
frame->fp = NULL;
108+
return 0;
109+
}
110+
111+
/*
112+
* Verify strictly increasing consecutive values. Since the stack
91113
* grows downward, walking the call chain in reverse must yield ever
92114
* increasing frame pointers.
93115
*/
94-
if (ofp >= frame->fp) {
95-
if (((uintptr_t)frame->fp & 0xf) &&
96-
current->dt_task && current->dt_task->dt_ustack == ofp) {
97-
frame->fp = NULL;
98-
return 0;
99-
}
100-
116+
if (ofp >= frame->fp)
101117
return -EINVAL;
102-
}
103118

104119
return 0;
105120
}
@@ -171,12 +186,15 @@ void dtrace_user_stacktrace(stacktrace_state_t *st)
171186
if (uprobe_return_addr_is_hijacked(frame.lr))
172187
fixups++;
173188

189+
if (frame.fp == NULL)
190+
break;
191+
174192
if (dtrace_unwind_frame(&frame) < 0) {
175193
this_cpu_core->cpuc_dtrace_illval = (uintptr_t)frame.fp;
176194
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADSTACK);
177195
break;
178196
}
179-
} while (frame.fp);
197+
} while (frame.lr);
180198

181199
patches = 0;
182200
for (ri = rilist; ri != NULL; ri = ri->next)
@@ -214,16 +232,20 @@ void dtrace_user_stacktrace(stacktrace_state_t *st)
214232

215233
if (pcs)
216234
*pcs++ = frame.lr;
235+
217236
limit--;
218237
st->depth++;
219238

220239
skip_frame:
240+
if (frame.fp == NULL)
241+
break;
242+
221243
if (dtrace_unwind_frame(&frame) < 0) {
222244
this_cpu_core->cpuc_dtrace_illval = (uintptr_t)frame.fp;
223245
DTRACE_CPUFLAG_SET(CPU_DTRACE_BADSTACK);
224246
break;
225247
}
226-
} while (limit && frame.fp);
248+
} while (limit);
227249

228250
out:
229251
if (pcs) {

0 commit comments

Comments
 (0)