aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchell Horne <mhorne@FreeBSD.org>2021-02-09 18:29:38 +0000
committerMitchell Horne <mhorne@FreeBSD.org>2021-02-17 16:05:00 +0000
commitde2b9422807586d376ec7ffa7b660cd492464bdf (patch)
tree035ff8007908be5c5921076839aba83bcb1873ee
parent8ba333e02eaa59337a4e1d5534d4e894344c8226 (diff)
downloadsrc-de2b9422807586d376ec7ffa7b660cd492464bdf.tar.gz
src-de2b9422807586d376ec7ffa7b660cd492464bdf.zip
arm64: validate breakpoint registers
In particular, we want to disallow setting breakpoints on kernel addresses from userspace. The control register fields are validated or ignored as appropriate. Reviewed by: markj MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D28560
-rw-r--r--sys/arm64/arm64/machdep.c37
-rw-r--r--sys/arm64/include/armreg.h17
2 files changed, 50 insertions, 4 deletions
diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c
index 90fc19d57415..bf44dba19482 100644
--- a/sys/arm64/arm64/machdep.c
+++ b/sys/arm64/arm64/machdep.c
@@ -357,6 +357,8 @@ int
set_dbregs(struct thread *td, struct dbreg *regs)
{
struct debug_monitor_state *monitor;
+ uint64_t addr;
+ uint32_t ctrl;
int count;
int i;
@@ -364,11 +366,38 @@ set_dbregs(struct thread *td, struct dbreg *regs)
count = 0;
monitor->dbg_enable_count = 0;
for (i = 0; i < DBG_BRP_MAX; i++) {
- /* TODO: Check these values */
- monitor->dbg_bvr[i] = regs->db_regs[i].dbr_addr;
- monitor->dbg_bcr[i] = regs->db_regs[i].dbr_ctrl;
- if ((monitor->dbg_bcr[i] & 1) != 0)
+ addr = regs->db_regs[i].dbr_addr;
+ ctrl = regs->db_regs[i].dbr_ctrl;
+
+ /* Don't let the user set a breakpoint on a kernel address. */
+ if (addr >= VM_MAXUSER_ADDRESS)
+ return (EINVAL);
+
+ /*
+ * The lowest 2 bits are ignored, so record the effective
+ * address.
+ */
+ addr = rounddown2(addr, 4);
+
+ /*
+ * Some control fields are ignored, and other bits reserved.
+ * Only unlinked, address-matching breakpoints are supported.
+ *
+ * XXX: fields that appear unvalidated, such as BAS, have
+ * constrained undefined behaviour. If the user mis-programs
+ * these, there is no risk to the system.
+ */
+ ctrl &= DBG_BCR_EN | DBG_BCR_PMC | DBG_BCR_BAS;
+ if ((ctrl & DBG_BCR_EN) != 0) {
+ /* Only target EL0. */
+ if ((ctrl & DBG_BCR_PMC) != DBG_BCR_PMC_EL0)
+ return (EINVAL);
+
monitor->dbg_enable_count++;
+ }
+
+ monitor->dbg_bvr[i] = addr;
+ monitor->dbg_bcr[i] = ctrl;
}
if (monitor->dbg_enable_count > 0)
monitor->dbg_flags |= DBGMON_ENABLED;
diff --git a/sys/arm64/include/armreg.h b/sys/arm64/include/armreg.h
index f5d25a572466..f2bce02782ec 100644
--- a/sys/arm64/include/armreg.h
+++ b/sys/arm64/include/armreg.h
@@ -944,6 +944,23 @@
#define DBG_MDSCR_KDE (0x1 << 13)
#define DBG_MDSCR_MDE (0x1 << 15)
+/* Debug Breakpoint Control Registers */
+#define DBG_BCR_EN 0x1
+#define DBG_BCR_PMC_SHIFT 1
+#define DBG_BCR_PMC (0x3 << DBG_BCR_PMC_SHIFT)
+#define DBG_BCR_PMC_EL1 (0x1 << DBG_BCR_PMC_SHIFT)
+#define DBG_BCR_PMC_EL0 (0x2 << DBG_BCR_PMC_SHIFT)
+#define DBG_BCR_BAS_SHIFT 5
+#define DBG_BCR_BAS (0xf << DBG_BCR_BAS_SHIFT)
+#define DBG_BCR_HMC_SHIFT 13
+#define DBG_BCR_HMC (0x1 << DBG_BCR_HMC_SHIFT)
+#define DBG_BCR_SSC_SHIFT 14
+#define DBG_BCR_SSC (0x3 << DBG_BCR_SSC_SHIFT)
+#define DBG_BCR_LBN_SHIFT 16
+#define DBG_BCR_LBN (0xf << DBG_BCR_LBN_SHIFT)
+#define DBG_BCR_BT_SHIFT 20
+#define DBG_BCR_BT (0xf << DBG_BCR_BT_SHIFT)
+
/* Perfomance Monitoring Counters */
#define PMCR_E (1 << 0) /* Enable all counters */
#define PMCR_P (1 << 1) /* Reset all counters */