[DTrace-devel] [PATCH] ERROR probe implementation
Eugene Loh
eugene.loh at oracle.com
Mon Jan 25 11:43:31 PST 2021
On 1/21/21 2:04 AM, Kris Van Hees wrote:
> On Wed, Jan 20, 2021 at 11:23:59PM -0500, Eugene Loh wrote:
>> Why was dt_cg_node()'s DT_TOK_PTR/DT_TOK_DOT case handling a NULL
>> pointer specially, and why has this handling been changed? I realize
>> that NULL pointers are bad addresses, but they are certainly not the
>> only ones. This is only one very special case.
> BPF requires that pointers are validated to not be NULL. And since a NULL
> pointer is bad news, it is appropriate to report it as a BADADDR fault.
>
> Sure, there are many other addresses that are bad addresses as well, but we
> currently do not have a mechanism to validate them. As we implement proper
> kernel memory accesses vs BPF program data accesses, support for more generic
> BADADDR faults will be added.
>
>> Somewhat related to the above, why are tests changed from
>> - *(char *)NULL;
>> + trace(((struct task_struct *)NULL)->pid);
>> I guess part of the issue is that some of these constructs will be
>> caught by the BPF verifier.
> Very simple... *(char *)NULL generates code that will not pass the BPF
> verifier because we do not generate correct code for it (yet) whereas we
> do generate valid code for ((struct task_struct *)NULL)->pid. The test is
> about the ERROR probe firing with a BADADDR fault, so the mechanism for
> triggering that is not relevant - picking one that actually works for the
> test is prudent.
I wanted to revisit these issues.
FWIW our docs say that "*(char *)NULL" should trigger the ERROR probe.
https://docs.oracle.com/en/operating-systems/oracle-linux/dtrace-guide/dt_prov.html#dt_error_prov
The "*(char *)NULL" construct is the quintessential example -- both in
our docs and in our test suite -- of how to trigger ERROR. But it
doesn't work that way now because the BPF verifier complains.
But the BPF verifier complaint is only because of the way we handle this
D code. Just as with DT_TOK_DOT and DT_TOK_PTR, we could (and for back
compatibility should) handle NULL dereference ourselves.
What do you think about adding such a change?
case DT_TOK_DEREF:
dt_cg_node(dnp->dn_child, dlp, drp);
dnp->dn_reg = dnp->dn_child->dn_reg;
if (!(dnp->dn_flags & DT_NF_REF)) {
uint_t ubit;
+ uint_t ok = dt_irlist_label(dlp);
/*
* Save and restore DT_NF_USERLAND across
dt_cg_load():
* we need the sign bit from dnp and the user
bit from
* dnp->dn_child in order to get the proper opcode.
*/
ubit = dnp->dn_flags & DT_NF_USERLAND;
dnp->dn_flags |=
(dnp->dn_child->dn_flags & DT_NF_USERLAND);
+ /* if NULL pointer, report BADADDR */
+ ok = dt_irlist_label(dlp);
+ emit(dlp, BPF_BRANCH_IMM(BPF_JNE, dnp->dn_reg,
0, ok));
+ dt_cg_probe_error(yypcb, -1, DTRACEFLT_BADADDR, 0);
+ emitl(dlp, ok,
+ BPF_NOP());
+
/* FIXME: Does not handled signed or userland */
emit(dlp, BPF_LOAD(dt_cg_load(dnp, ctfp,
dnp->dn_type), dnp->dn_reg, dnp->dn_reg, 0));
dnp->dn_flags &= ~DT_NF_USERLAND;
dnp->dn_flags |= ubit;
}
break;
More information about the DTrace-devel
mailing list