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-24 14:57:04 +0000
commit8837e9c54072679b69ae0c0345e7ef7d241255aa (patch)
tree5cbede2f582fe9f823cf7bc79fb47a6a01733aae
parent98e40918a6fa5113404b0fae15707a9804f447bd (diff)
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 Sponsored by: The FreeBSD Foundation (cherry picked from commit de2b9422807586d376ec7ffa7b660cd492464bdf)
-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 201d7559320b..73d1010057b9 100644
--- a/sys/arm64/include/armreg.h
+++ b/sys/arm64/include/armreg.h
@@ -943,6 +943,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 */