// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com> * * Selftests for breakpoints (and more generally the do_debug() path) in x86.
*/
/* * Ensures the child and parent are always "talking" about * the same test sequence. (ie: that we haven't forgotten * to call check_trapped() somewhere).
*/ staticint nr_tests;
staticvoid set_breakpoint_addr(void *addr, int n)
{ int ret;
ret = ptrace(PTRACE_POKEUSER, child_pid,
offsetof(struct user, u_debugreg[n]), addr); if (ret)
ksft_exit_fail_msg("Can't set breakpoint addr: %s\n",
strerror(errno));
}
staticvoid toggle_breakpoint(int n, int type, int len, int local, int global, int set)
{ int ret;
int xtype, xlen; unsignedlong vdr7, dr7;
switch (type) { case BP_X:
xtype = 0; break; case BP_W:
xtype = 1; break; case BP_RW:
xtype = 3; break;
}
switch (len) { case 1:
xlen = 0; break; case 2:
xlen = 4; break; case 4:
xlen = 0xc; break; case 8:
xlen = 8; break;
}
staticvoid check_trapped(void)
{ /* * If we haven't trapped, wake up the parent * so that it notices the failure.
*/ if (!trapped)
kill(getpid(), SIGUSR1);
trapped = 0;
nr_tests++;
}
staticvoid write_var(int len)
{ char *pcval; short *psval; int *pival; longlong *plval; int i;
for (i = 0; i < 4; i++) { switch (len) { case 1:
pcval = (char *)&dummy_var[i];
*pcval = 0xff; break; case 2:
psval = (short *)&dummy_var[i];
*psval = 0xffff; break; case 4:
pival = (int *)&dummy_var[i];
*pival = 0xffffffff; break; case 8:
plval = (longlong *)&dummy_var[i];
*plval = 0xffffffffffffffffLL; break;
}
check_trapped();
}
}
staticvoid read_var(int len)
{ char cval; short sval; int ival; longlong lval; int i;
for (i = 0; i < 4; i++) { switch (len) { case 1:
cval = *(char *)&dummy_var[i]; break; case 2:
sval = *(short *)&dummy_var[i]; break; case 4:
ival = *(int *)&dummy_var[i]; break; case 8:
lval = *(longlong *)&dummy_var[i]; break;
}
check_trapped();
}
}
/* * Do the r/w/x accesses to trigger the breakpoints. And run * the usual traps.
*/ staticvoid trigger_tests(void)
{ int len, local, global, i; char val; int ret;
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); if (ret) {
ksft_print_msg("Can't be traced? %s\n", strerror(errno)); return;
}
/* Wake up father so that it sets up the first test */
kill(getpid(), SIGUSR1);
/* Test instruction breakpoints */ for (local = 0; local < 2; local++) { for (global = 0; global < 2; global++) { if (!local && !global) continue;
for (i = 0; i < COUNT_ISN_BPS; i++) {
dummy_funcs[i]();
check_trapped();
}
}
}
/* Test write watchpoints */ for (len = 1; len <= sizeof(long); len <<= 1) { for (local = 0; local < 2; local++) { for (global = 0; global < 2; global++) { if (!local && !global) continue;
write_var(len);
}
}
}
/* Test read/write watchpoints (on read accesses) */ for (len = 1; len <= sizeof(long); len <<= 1) { for (local = 0; local < 2; local++) { for (global = 0; global < 2; global++) { if (!local && !global) continue;
read_var(len);
}
}
}
for (i = 0; i < COUNT_WPS; i++) {
set_breakpoint_addr(&dummy_var[i], i);
toggle_breakpoint(i, mode, len, local, global, 1);
ptrace(PTRACE_CONT, child_pid, NULL, 0);
sprintf(buf, "Test %s watchpoint %d with len: %d local: %d global: %d\n",
mode_str, i, len, local, global);
check_success(buf);
toggle_breakpoint(i, mode, len, local, global, 0);
}
}
/* Set the breakpoints and check the child successfully trigger them */ staticvoid launch_tests(void)
{ char buf[1024]; unsignedint tests = 0; int len, local, global, i;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.