Bug 298064 - arm: dtrace copyinto causes a kernel mode data abort: 'Translation Fault (L2)' on read
Summary: arm: dtrace copyinto causes a kernel mode data abort: 'Translation Fault (L2)...
Status: In Progress
Alias: None
Product: Base System
Classification: Unclassified
Component: arm (show other bugs)
Version: Unspecified
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-arm (Nobody)
URL: https://reviews.freebsd.org/D59279
Keywords: crash
Depends on:
Blocks:
 
Reported: 2026-08-30 22:20 UTC by Benjamin Jacobs
Modified: 2026-09-03 15:09 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Benjamin Jacobs 2026-08-30 22:20:43 UTC
Running `dwatch -X read` on a cubieboard (Cortex-A8) results in the
following panic:

Fatal kernel mode data abort: 'Translation Fault (L2)' on read
trapframe: 0xd40b3948
FSR=00000007, FAR=20f49780, spsr=200000d3
r0 =20f49780, r1 =dba75010, r2 =00008000, r3 =bfc00000
r4 =d9596098, r5 =00000000, r6 =d40b3b10, r7 =00000001
r8 =d768281c, r9 =d9596098, r10=00000003, r11=d40b3a50
r12=d95e01a8, ssp=d40b39d8, slr=d95bde84, pc =d95ce09c

panic: Fatal abort
cpuid = 0
time = 1787862210
KDB: stack backtrace:
db_trace_self() at db_trace_self
	 pc = 0xc06df164  lr = 0xc0089bc0 (db_trace_self_wrapper+0x40)
	 sp = 0xd40b3720  fp = 0xd40b3838
db_trace_self_wrapper() at db_trace_self_wrapper+0x40
	 pc = 0xc0089bc0  lr = 0xc0361940 (vpanic+0x15c)
	 sp = 0xd40b3840  fp = 0xd40b3860
	 r4 = 0x00000100  r5 = 0xc07e3bb2
	 r6 = 0xc0c09d24  r7 = 0x00000000
vpanic() at vpanic+0x15c
	 pc = 0xc0361940  lr = 0xc03617e4 (vpanic)
	 sp = 0xd40b3868  fp = 0xd40b386c
	 r4 = 0xd40b3948  r5 = 0x00000013
	 r6 = 0x20f49780  r7 = 0x00000007
	 r8 = 0x00000007  r9 = 0x00000013
	r10 = 0x20f49780
vpanic() at vpanic
	 pc = 0xc03617e4  lr = 0xc0705cb4 (abort_align)
	 sp = 0xd40b3874  fp = 0xd40b38a0
	 r4 = 0x00000007  r5 = 0x00000007
	 r6 = 0x00000013  r7 = 0x20f49780
	 r8 = 0xd40b386c  r9 = 0xc03617e4
	r10 = 0xd40b3874
abort_align() at abort_align
	 pc = 0xc0705cb4  lr = 0xc070592c (abort_handler+0x4d0)
	 sp = 0xd40b38a8  fp = 0xd40b3940
	 r4 = 0xc4d9a400 r10 = 0x20f49780
abort_handler() at abort_handler+0x4d0
	 pc = 0xc070592c  lr = 0xc06e1ae4 (exception_exit)
	 sp = 0xd40b3948  fp = 0xd40b3a50
	 r4 = 0xd9596098  r5 = 0x00000000
	 r6 = 0xd40b3b10  r7 = 0x00000001
	 r8 = 0xd768281c  r9 = 0xd9596098
	r10 = 0x00000003
exception_exit() at exception_exit
	 pc = 0xc06e1ae4  lr = 0xd95bde84 ($a+0xe98)
	 sp = 0xd40b39d8  fp = 0xd40b3a50
	 r0 = 0x20f49780  r1 = 0xdba75010
	 r2 = 0x00008000  r3 = 0xbfc00000
	 r4 = 0xd9596098  r5 = 0x00000000
	 r6 = 0xd40b3b10  r7 = 0x00000001
	 r8 = 0xd768281c  r9 = 0xd9596098
	r10 = 0x00000003 r12 = 0xd95e01a8
dtrace_copy() at dtrace_copy+0x10
	 pc = 0xd95ce09c  lr = 0xd95bde84 ($a+0xe98)
	 sp = 0xd40b39d8  fp = 0xd40b3a50
$a() at $a+0xe98
	 pc = 0xd95bde84  lr = 0xd95b488c ($a+0xa8)
	 sp = 0xd40b3a58  fp = 0xd40b3bc8
	 r4 = 0x00000011  r5 = 0xd95b39e4
	 r6 = 0x00000000  r7 = 0x00000001
	 r8 = 0x00000014  r9 = 0x00000013
	r10 = 0xd40b3c90
$a() at $a+0xa8
	 pc = 0xd95b488c  lr = 0xd95b19ec (dtrace_probe+0x8e4)
	 sp = 0xd40b3bd0  fp = 0xd40b3d40
	 r4 = 0xdba75010  r5 = 0x000013fe
	 r6 = 0xdba6c2c0  r7 = 0xdba71340
	 r8 = 0x00000000  r9 = 0xdba71240
	r10 = 0xdba71380
dtrace_probe() at dtrace_probe+0x8e4
	 pc = 0xd95b19ec  lr = 0xdafa0f08 ($a+0xec)
	 sp = 0xd40b3d48  fp = 0xd40b3db8
	 r4 = 0x00008a47  r5 = 0x00000000
	 r6 = 0xd40b3d60  r7 = 0xdafa4cf0
	 r8 = 0x20f49780  r9 = 0x00000003
	r10 = 0xc4d98000
$a() at $a+0xec
	 pc = 0xdafa0f08  lr = 0xc07053fc (swi_handler+0x8a0)
	 sp = 0xd40b3dc0  fp = 0xd40b3e38
	 r4 = 0xc4d9a400  r5 = 0x00000000
	 r6 = 0xc09d64ac  r7 = 0x00000001
	 r8 = 0xc4d9a6dc  r9 = 0x00000000
swi_handler() at swi_handler+0x8a0
	 pc = 0xc07053fc  lr = 0xc06e1a78 (swi_exit)
	 sp = 0xd40b3e40  fp = 0xbfbfea58
	 r4 = 0x20b05fa0  r5 = 0x00000000
	 r6 = 0x000001fe  r7 = 0x00000003
	 r8 = 0x20f46000  r9 = 0x20b05fa0
	r10 = 0x20f46000
swi_exit() at swi_exit
	 pc = 0xc06e1a78  lr = 0xc06e1a78 (swi_exit)
	 sp = 0xd40b3e40  fp = 0xbfbfea58
KDB: enter: panic
[ thread pid 2638 tid 100091 ]
Stopped at      kdb_enter+0x54: ldrb    r15, [r15, r15, ror r15]!

After investigation I've found 3 issues which, once fixed, solve this
problem.

Firstly, reading user memory from dtrace_copy (DIR-copyinto) should be
done using the unprivileged ARM instructions.

Secondly, the call into the DTrace hook is passing the value of the
FAR instead of the fault type identifier, preventing the hook from
executing the correct code-path and to, ultimately, recover from the
fault.

(BTW, there is another typo which would have de-facto prevented the
handling of translation faults, so this doesn't seem to be a
regression from 2014 when there seems to have been some
back-and-forth w.r.t. the passing or not of the fault number as direct
argument to the dtrace_trap function)

At last, the call into the DTrace hook happens fairly late in the flow
of the abort handler. This is likely wrong because this is not how all
the other architectures do, nor how the 2005 DTrace paper describes
the handling of unmapped memory: DTrace is expected to simply bypass
the fault handler over fault induced by DTrace and to advance the
instruction pointer over to the next instruction. No mapping should be
attempted.

With these issues fixed, the DIR copyinto function successfully
reports the invalid access instead of crashing the kernel and the
dwatch read/write/rw profiles work as expected!

Still, the faulting addresses are reported as NULL, but fixing this
requires adding the FAR to the ARM trapframe. This seems to be quite
easy and low risk to do: the tf_pad field could potentially be
recycled w/o having to change the assembler entry and exit points. But
I'm unsure how useful it is for DTrace to have this
information. Given my understanding of AArch64 and RISC-V, both
reporting the last faulting address (instead of the first, on amd64),
the usefulness of having a precise address reported is likely to be
pretty low.

The easter-egg of this investigation is that both AArch64 and RISC-V
suffer from the same pessimization in dtrace_copy: they keep faulting
on the successive bytes. In my test, it was not unusual to see
32768-bytes long copies to be attempted*. I've rewritten the ARM32
dtrace_copy to detect the trap and abort the copy early. AArch64 and
RISC-V could likely be de-pessimized in a similar way. Otherwise, it
is just an useless and a wasteful usage of cycles.

Hopefully, the previous paragraph will reinforce the interest in
keeping the armv7 architecture alive. It is a low-overhead,
affordable, accessible, easily understandable and hackable
architecture, ideal for learning and that results in bringing
improvements to the benefit of all.

I'll add a link towards a review with my fixes to this PR, to tie-in
the circular reference.

Cheers,

Benjamin


*: A funny anecdote is that I mistook the successive faulting for a
forever trap-loop.  I pulled my hair, lost more sleep, asked
existential questions before finally realizing that there were no bugs
anymore to be found (or not there, at least...).
Comment 1 commit-hook freebsd_committer freebsd_triage 2026-09-03 15:06:26 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=3e6bc5c3b5632ac9f55633b7d4e2e1a42b96a78b

commit 3e6bc5c3b5632ac9f55633b7d4e2e1a42b96a78b
Author:     Benjamin Jacobs <freebsd@dev.thsi.be>
AuthorDate: 2026-09-03 13:52:19 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-03 15:05:03 +0000

    sys/arm: Fix DTrace trap hook

    Move the dtrace_trap hook at the start of the abort handler to exit
    early when a trap is handled by DTrace.

    Fix the type argument to be the actual fault type instead of the value
    of the FAR. The latter will need to be added to the trapframe, until
    then DTrace will report unmapped addresses as the null address.

    Correct the comment of the PUSHFRAMEINSVC assembler macro to reflect
    that coming from SVC32 mode is expected for DTrace traps.

    PR:             298064
    MFC after:      1 month
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D59279

 sys/arm/arm/exception.S |  6 +++---
 sys/arm/arm/trap-v6.c   | 15 ++++++++-------
 2 files changed, 11 insertions(+), 10 deletions(-)
Comment 2 commit-hook freebsd_committer freebsd_triage 2026-09-03 15:06:30 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=00678f5e5c08ec7949e2f57d78faeaf266a0f17a

commit 00678f5e5c08ec7949e2f57d78faeaf266a0f17a
Author:     Benjamin Jacobs <freebsd@dev.thsi.be>
AuthorDate: 2026-09-03 13:52:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-03 15:05:03 +0000

    dtrace/arm: Really trap translation faults

    Fix the constant case label to properly handle translation faults
    caused by DTrace probes. Alignment errors are not expected to be
    generated, so stop handling them.

    While at it, correct an amd64-specific comment and add a comment
    regarding the missing faulting address which could be addressed by a
    later improvement.

    PR:             298064
    MFC after:      1 month
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D59281

 sys/cddl/dev/dtrace/arm/dtrace_subr.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
Comment 3 commit-hook freebsd_committer freebsd_triage 2026-09-03 15:06:31 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=c874d312e78fc60e6731a043fc45b47165618370

commit c874d312e78fc60e6731a043fc45b47165618370
Author:     Benjamin Jacobs <freebsd@dev.thsi.be>
AuthorDate: 2026-09-03 13:52:21 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-03 15:05:03 +0000

    dtrace/arm: Fix and de-pessimize dtrace_copy

    Use an unprivileged load to access user memory from dtrace_copy, which
    is running in SVC mode.

    Abort the loop if the load is trapped, as it is useless, hence
    wasteful, to keep faulting on successive addresses. I believe that
    this de-pessimization should also be done on aarch64 and riscv.

    PR:             298064
    MFC after:      1 month
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D59280

 sys/cddl/dev/dtrace/arm/dtrace_asm.S | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)