Fix for the x86_64 "bt" command for kernels that are configured with

CONFIG_FRAME_POINTER.  Without the patch, the per-text-return-address
framesize cache may contain invalid entries for functions that have
an "and $0xfffffffffffffff0,%rsp" instruction in their prologue,
which aligns the stack on a 16-byte boundary; therefore any cached
framesize for a text-return-address in such a function may be
incorrect depending upon the alignment of the stack address of a
calling function.  If an invalid cached framesize is utilized by
"bt", the backtrace may skip over several frames, or may display
one or more invalid (stale) frames.  The patch introduces a new
cache that contains functions for which framesize values should
not be cached.
(anderson@redhat.com)
This commit is contained in:
Dave Anderson 2018-04-05 16:05:51 -04:00
parent 6088a29f7e
commit 33421b0b5a

144
x86_64.c
View File

@ -118,7 +118,8 @@ static void x86_64_init_hyper(int);
static ulong x86_64_get_stackbase_hyper(ulong);
static ulong x86_64_get_stacktop_hyper(ulong);
static int x86_64_framesize_cache_resize(void);
static int x86_64_framesize_cache_func(int, ulong, int *, int);
static int x86_64_do_not_cache_framesize(struct syment *, ulong);
static int x86_64_framesize_cache_func(int, ulong, int *, int, struct syment *);
static ulong x86_64_get_framepointer(struct bt_info *, ulong);
int search_for_eframe_target_caller(struct bt_info *, ulong, int *);
static int x86_64_get_framesize(struct bt_info *, ulong, ulong);
@ -7843,8 +7844,90 @@ x86_64_framesize_cache_resize(void)
return TRUE;
}
ulong *x86_64_framesize_no_cache = NULL;
static int framesize_no_cache_entries = 0;
#define FRAMESIZE_NO_CACHE_INCR (10)
static int
x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int exception)
x86_64_do_not_cache_framesize(struct syment *sp, ulong textaddr)
{
int c, instr, arg;
char buf[BUFSIZE];
char *arglist[MAXARGS];
ulong *new_fnc;
if (x86_64_framesize_no_cache[framesize_no_cache_entries-1]) {
if ((new_fnc = realloc(x86_64_framesize_no_cache,
(framesize_no_cache_entries+FRAMESIZE_NO_CACHE_INCR) *
sizeof(ulong))) == NULL) {
error(INFO, "cannot realloc x86_64_framesize_no_cache space!\n");
return FALSE;
}
x86_64_framesize_no_cache = new_fnc;
for (c = framesize_no_cache_entries;
c < framesize_no_cache_entries + FRAMESIZE_NO_CACHE_INCR; c++)
x86_64_framesize_no_cache[c] = 0;
framesize_no_cache_entries += FRAMESIZE_NO_CACHE_INCR;
}
for (c = 0; c < framesize_no_cache_entries; c++)
if (x86_64_framesize_no_cache[c] == sp->value)
return TRUE;
if (!accessible(sp->value))
return FALSE;
sprintf(buf, "disassemble 0x%lx,0x%lx", sp->value, textaddr);
open_tmpfile2();
if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) {
close_tmpfile2();
return FALSE;
}
rewind(pc->tmpfile2);
instr = arg = -1;
while (fgets(buf, BUFSIZE, pc->tmpfile2)) {
if (STRNEQ(buf, "Dump of assembler code"))
continue;
else if (STRNEQ(buf, "End of assembler dump."))
break;
else if ((c = parse_line(buf, arglist)) < 3)
continue;
if (instr == -1) {
if (LASTCHAR(arglist[0]) == ':') {
instr = 1;
arg = 2;
} else {
instr = 2;
arg = 3;
}
}
if (STREQ(arglist[instr], "and") &&
STREQ(arglist[arg], "$0xfffffffffffffff0,%rsp")) {
close_tmpfile2();
for (c = 0; c < framesize_no_cache_entries; c++) {
if (x86_64_framesize_no_cache[c] == 0) {
x86_64_framesize_no_cache[c] = sp->value;
break;
}
}
return TRUE;
}
if (STREQ(arglist[instr], "callq"))
break;
}
close_tmpfile2();
return FALSE;
}
static int
x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int exception, struct syment *sp)
{
int i, n;
struct framesize_cache *fc;
@ -7856,6 +7939,10 @@ x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int excepti
sizeof(struct framesize_cache))) == NULL)
error(FATAL,
"cannot calloc x86_64_framesize_cache space!\n");
framesize_no_cache_entries = FRAMESIZE_NO_CACHE_INCR;
if ((x86_64_framesize_no_cache = calloc(framesize_no_cache_entries,
sizeof(ulong))) == NULL)
error(FATAL, "cannot calloc x86_64_framesize_no_cache space!\n");
}
switch (cmd)
@ -7873,6 +7960,8 @@ x86_64_framesize_cache_func(int cmd, ulong textaddr, int *framesize, int excepti
return FALSE;
case FRAMESIZE_ENTER:
if (sp && x86_64_do_not_cache_framesize(sp, textaddr))
return *framesize;
retry:
fc = &x86_64_framesize_cache[0];
for (i = 0; i < framesize_cache_entries; i++, fc++) {
@ -7901,21 +7990,36 @@ retry:
return *framesize;
case FRAMESIZE_DUMP:
fprintf(fp, "framesize_cache_entries:\n");
fc = &x86_64_framesize_cache[0];
for (i = 0; i < framesize_cache_entries; i++, fc++) {
if (fc->textaddr == 0) {
if (i < (framesize_cache_entries-1)) {
fprintf(fp, "[%d-%d]: (unused)\n",
fprintf(fp, " [%d-%d]: (unused)\n",
i, framesize_cache_entries-1);
}
break;
}
fprintf(fp, "[%3d]: %lx %3d %s (%s)\n", i,
fprintf(fp, " [%3d]: %lx %3d %s (%s)\n", i,
fc->textaddr, fc->framesize,
fc->exception ? "EX" : "CF",
value_to_symstr(fc->textaddr, buf, 0));
}
fprintf(fp, "\nframesize_no_cache_entries:\n");
for (i = 0; i < framesize_no_cache_entries; i++) {
if (x86_64_framesize_no_cache[i])
fprintf(fp, " [%3d]: %lx (%s)\n",
i, x86_64_framesize_no_cache[i],
value_to_symstr(x86_64_framesize_no_cache[i], buf, 0));
else {
fprintf(fp, " [%d-%d]: (unused)\n",
i, framesize_no_cache_entries-1);
break;
}
}
break;
}
@ -8044,7 +8148,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp)
if (!(bt->flags & BT_FRAMESIZE_DEBUG) &&
x86_64_framesize_cache_func(FRAMESIZE_QUERY, textaddr, &framesize,
exception)) {
exception, NULL)) {
if (framesize == -1)
bt->flags |= BT_FRAMESIZE_DISABLE;
return framesize;
@ -8081,7 +8185,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp)
else
framesize = 8;
return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr,
&framesize, exception));
&framesize, exception, NULL));
}
if ((machdep->flags & FRAMEPOINTER) &&
@ -8099,7 +8203,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp)
if (framepointer) {
framesize = framepointer - rsp;
return (x86_64_framesize_cache_func(FRAMESIZE_ENTER,
textaddr, &framesize, 0));
textaddr, &framesize, 0, sp));
}
}
@ -8117,7 +8221,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp)
if ((korc->type == ORC_TYPE_CALL) && (korc->sp_reg == ORC_REG_SP)) {
framesize = (korc->sp_offset - 8);
return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr,
&framesize, exception));
&framesize, exception, NULL));
}
}
@ -8238,7 +8342,7 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp)
textaddr = textaddr_save;
return (x86_64_framesize_cache_func(FRAMESIZE_ENTER, textaddr,
&framesize, exception));
&framesize, exception, NULL));
}
static void
@ -8252,18 +8356,20 @@ x86_64_framesize_debug(struct bt_info *bt)
switch (bt->hp->esp)
{
case 1: /* "dump" */
x86_64_framesize_cache_func(FRAMESIZE_DUMP, 0, NULL, 0);
x86_64_framesize_cache_func(FRAMESIZE_DUMP, 0, NULL, 0, NULL);
break;
case 0:
if (bt->hp->eip) { /* clear one entry */
framesize = -1;
x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip,
&framesize, exception);
&framesize, exception, NULL);
} else { /* clear all entries */
BZERO(&x86_64_framesize_cache[0],
sizeof(struct framesize_cache)*framesize_cache_entries);
fprintf(fp, "framesize cache cleared\n");
BZERO(&x86_64_framesize_no_cache[0],
sizeof(ulong)*framesize_no_cache_entries);
fprintf(fp, "framesize caches cleared\n");
}
break;
@ -8278,16 +8384,20 @@ x86_64_framesize_debug(struct bt_info *bt)
machdep->flags |= FRAMEPOINTER;
BZERO(&x86_64_framesize_cache[0],
sizeof(struct framesize_cache)*framesize_cache_entries);
BZERO(&x86_64_framesize_no_cache[0],
sizeof(ulong)*framesize_no_cache_entries);
fprintf(fp,
"framesize cache cleared and FRAMEPOINTER turned ON\n");
"framesize caches cleared and FRAMEPOINTER turned ON\n");
break;
case -4:
machdep->flags &= ~FRAMEPOINTER;
BZERO(&x86_64_framesize_cache[0],
sizeof(struct framesize_cache)*framesize_cache_entries);
BZERO(&x86_64_framesize_no_cache[0],
sizeof(ulong)*framesize_no_cache_entries);
fprintf(fp,
"framesize cache cleared and FRAMEPOINTER turned OFF\n");
"framesize caches cleared and FRAMEPOINTER turned OFF\n");
break;
case -5:
@ -8302,7 +8412,7 @@ x86_64_framesize_debug(struct bt_info *bt)
framesize = bt->hp->esp;
if (bt->hp->eip)
x86_64_framesize_cache_func(FRAMESIZE_ENTER, bt->hp->eip,
&framesize, exception);
&framesize, exception, NULL);
} else
error(INFO, "x86_64_framesize_debug: ignoring command\n");
break;
@ -8329,7 +8439,7 @@ __schedule_frame_adjust(ulong rsp_in, struct bt_info *bt)
rsp_in, bt->stackbase, bt->stacktop, bt->tc->processor);
if (x86_64_framesize_cache_func(FRAMESIZE_QUERY,
machdep->machspec->thread_return, &framesize, 0)) {
machdep->machspec->thread_return, &framesize, 0, NULL)) {
rsp = rsp_in + framesize;
i = (rsp - bt->stackbase)/sizeof(ulong);
up = (ulong *)(&bt->stackbuf[i*sizeof(ulong)]);
@ -8357,7 +8467,7 @@ __schedule_frame_adjust(ulong rsp_in, struct bt_info *bt)
bt->instptr = *up;
x86_64_framesize_cache_func(FRAMESIZE_ENTER,
machdep->machspec->thread_return,
&framesize, 0);
&framesize, 0, NULL);
bt->instptr = *up;
found = TRUE;
break;