Skip to content

Commit

Permalink
Support loading scalars from kernel addresses
Browse files Browse the repository at this point in the history
Data access for non-scalar datatypes was implemented using the
probe_read() BPF helper while scalar datatypes were accessed using
load and store instructions.  When loading scalar data from kernel
addresses, a load instruction cannot be used.  This patch ensures
that such accesses use the probe_read() BPF helper.  (Storing to
kernel addresses is not supported by DTrace so that case does not
need special handling.)

The new dt_cg_load_scalar() function implements the scalar kernel
access.

A new node flag is introduced: DT_NF_DPTR.  It is set for any pointer
that is known to be an internal pointer (i.e. address of a location
in a BPF map managed by DTrace).  Such pointers can be dereferenced
with a load instruction.  All other cases use dt_cg_load_scalar().
The DT_NF_DPTR is propagated across =, +, and -.

Comments and code about DT_NF_USERLAND save/restore across dt_cg_ldsize()
have been removed - they are no longer relevant.

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Reviewed-by: Eugene Loh <eugene.loh@oracle.com>
  • Loading branch information
kvanhees committed Jul 27, 2022
1 parent 63a1ab5 commit febf457
Show file tree
Hide file tree
Showing 40 changed files with 372 additions and 98 deletions.
83 changes: 44 additions & 39 deletions libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,30 @@ dt_cg_ldsize(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type, ssize_t *ret_size)
return ldstw[size];
}

static void
dt_cg_load_scalar(dt_node_t *dnp, uint_t op, ssize_t size, dt_irlist_t *dlp,
dt_regset_t *drp)
{
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
emit(dlp, BPF_MOV_REG(BPF_REG_3, dnp->dn_reg));
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
emit(dlp, BPF_MOV_IMM(BPF_REG_2, size));
dt_regset_xalloc(drp, BPF_REG_0);
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read));
dt_regset_free(drp, BPF_REG_0);
dt_regset_free_args(drp);
emit(dlp, BPF_LOAD(BPF_DW, dnp->dn_reg, BPF_REG_FP, DT_STK_SP));
emit(dlp, BPF_LOAD(op, dnp->dn_reg, dnp->dn_reg, 0));

if (dnp->dn_flags & DT_NF_SIGNED && size < sizeof(uint64_t)) {
int n = (sizeof(uint64_t) - size) * NBBY;

emit(dlp, BPF_ALU64_IMM(BPF_LSH, dnp->dn_reg, n));
emit(dlp, BPF_ALU64_IMM(BPF_ARSH, dnp->dn_reg, n));
}
}

static void
dt_cg_load_var(dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp)
{
Expand Down Expand Up @@ -2394,7 +2418,6 @@ dt_cg_field_set(dt_node_t *src, dt_irlist_t *dlp,
* r2 <<= shift
* r1 |= r2
*/
/* FIXME: Does not handle userland */
emit(dlp, BPF_LOAD(dt_cg_ldsize(dst, fp, m.ctm_type, NULL), r1, dst->dn_reg, 0));
dt_cg_setx(dlp, r2, cmask);
emit(dlp, BPF_ALU64_REG(BPF_AND, r1, r2));
Expand Down Expand Up @@ -5034,20 +5057,9 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
dnp->dn_reg = dnp->dn_child->dn_reg;

if (!(dnp->dn_flags & DT_NF_REF)) {
uint_t ubit;
uint_t op;
ssize_t size;

/*
* Save and restore DT_NF_USERLAND across
* dt_cg_ldsize(): 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);

dt_cg_check_notnull(dlp, drp, dnp->dn_reg);
op = dt_cg_ldsize(dnp, ctfp, dnp->dn_type, &size);

Expand All @@ -5063,11 +5075,10 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
dnp->dn_reg);
}

/* FIXME: Does not handled signed or userland */
emit(dlp, BPF_LOAD(op, dnp->dn_reg, dnp->dn_reg, 0));

dnp->dn_flags &= ~DT_NF_USERLAND;
dnp->dn_flags |= ubit;
if (dnp->dn_child->dn_flags & DT_NF_DPTR)
emit(dlp, BPF_LOAD(op, dnp->dn_reg, dnp->dn_reg, 0));
else
dt_cg_load_scalar(dnp, op, size, dlp, drp);
}
break;

Expand Down Expand Up @@ -5148,9 +5159,9 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)

case DT_TOK_PTR:
case DT_TOK_DOT: {

assert(dnp->dn_right->dn_kind == DT_NODE_IDENT);
dt_cg_node(dnp->dn_left, dlp, drp);

dt_cg_check_notnull(dlp, drp, dnp->dn_left->dn_reg);

/*
Expand Down Expand Up @@ -5210,24 +5221,15 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
}

if (!(dnp->dn_flags & DT_NF_REF)) {
uint_t ubit;

/*
* Save and restore DT_NF_USERLAND across
* dt_cg_ldsize(): we need the sign bit from dnp and
* the user bit from dnp->dn_left in order to get the
* proper opcode.
*/
ubit = dnp->dn_flags & DT_NF_USERLAND;
dnp->dn_flags |=
(dnp->dn_left->dn_flags & DT_NF_USERLAND);
uint_t op;
ssize_t size;

/* FIXME: Does not handle signed and userland */
emit(dlp, BPF_LOAD(dt_cg_ldsize(dnp, ctfp, m.ctm_type, NULL),
dnp->dn_left->dn_reg, dnp->dn_left->dn_reg, 0));
op = dt_cg_ldsize(dnp, ctfp, m.ctm_type, &size);

dnp->dn_flags &= ~DT_NF_USERLAND;
dnp->dn_flags |= ubit;
if (dnp->dn_left->dn_flags & DT_NF_DPTR)
emit(dlp, BPF_LOAD(op, dnp->dn_left->dn_reg, dnp->dn_left->dn_reg, 0));
else
dt_cg_load_scalar(dnp->dn_left, op, size, dlp, drp);

if (dnp->dn_flags & DT_NF_BITFIELD)
dt_cg_field_get(dnp, dlp, drp, ctfp, &m);
Expand Down Expand Up @@ -5330,13 +5332,16 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);

dt_cg_xsetx(dlp, dnp->dn_ident,
DT_LBL_NONE, dnp->dn_reg, sym.st_value);
dt_cg_xsetx(dlp, dnp->dn_ident, DT_LBL_NONE,
dnp->dn_reg, sym.st_value);

if (!(dnp->dn_flags & DT_NF_REF)) {
/* FIXME: NO signed or userland yet */
emit(dlp, BPF_LOAD(dt_cg_ldsize(dnp, ctfp, dnp->dn_type, NULL),
dnp->dn_reg, dnp->dn_reg, 0));
uint_t op;
ssize_t size;

op = dt_cg_ldsize(dnp, ctfp, dnp->dn_type, &size);

dt_cg_load_scalar(dnp, op, size, dlp, drp);
}
break;
}
Expand Down
43 changes: 29 additions & 14 deletions libdtrace/dt_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -2781,6 +2781,13 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
dnp->dn_ident = idp;
dnp->dn_flags |= DT_NF_LVALUE;

/*
* If the type of the variable is a REF-type, we mark this
* variable node as a pointer to DTrace-managed storage (DPTR).
*/
if (dnp->dn_flags & DT_NF_REF)
dnp->dn_flags |= DT_NF_DPTR;

if (idp->di_flags & DT_IDFLG_WRITE)
dnp->dn_flags |= DT_NF_WRITABLE;

Expand Down Expand Up @@ -2907,6 +2914,13 @@ dt_xcook_ident(dt_node_t *dnp, dt_idhash_t *dhp, uint_t idkind, int create)
dnp->dn_ident = idp;
dnp->dn_flags |= DT_NF_LVALUE | DT_NF_WRITABLE;

/*
* If the type of the variable is a REF-type, we mark this
* variable node as a pointer to DTrace-managed storage (DPTR).
*/
if (dnp->dn_flags & DT_NF_REF)
dnp->dn_flags |= DT_NF_DPTR;

/*
* Still a good idea, but not relevant for assignments: see
* dt_cook_ident:DT_TOK_ASGN for more. In particular, this is
Expand Down Expand Up @@ -3235,7 +3249,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
ctf_membinfo_t m;
ctf_file_t *ctfp;
ctf_id_t type;
int kind, val, uref;
int kind, val, xflags;
dt_ident_t *idp;

char n1[DT_TYPE_NAMELEN];
Expand Down Expand Up @@ -3465,20 +3479,20 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)

if (lp_is_int && rp_is_int) {
dt_type_promote(lp, rp, &ctfp, &type);
uref = 0;
xflags = 0;
} else if (lp_is_ptr && rp_is_int) {
ctfp = lp->dn_ctfp;
type = lp->dn_type;
uref = lp->dn_flags & DT_NF_USERLAND;
xflags = lp->dn_flags & (DT_NF_USERLAND | DT_NF_DPTR);
} else if (lp_is_int && rp_is_ptr && op == DT_TOK_ADD) {
ctfp = rp->dn_ctfp;
type = rp->dn_type;
uref = rp->dn_flags & DT_NF_USERLAND;
xflags = rp->dn_flags & (DT_NF_USERLAND | DT_NF_DPTR);
} else if (lp_is_ptr && rp_is_ptr && op == DT_TOK_SUB &&
dt_node_is_ptrcompat(lp, rp, NULL, NULL)) {
ctfp = dtp->dt_ddefs->dm_ctfp;
type = ctf_lookup_by_name(ctfp, "ptrdiff_t");
uref = 0;
xflags = 0;
} else
xyerror(D_OP_INCOMPAT, "operands have incompatible "
"types: \"%s\" %s \"%s\"\n",
Expand All @@ -3503,7 +3517,6 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
/*
* Array bounds-checking. (Non-associative arrays only.)
*/

artype = ctf_type_resolve(lp->dn_ctfp, lp->dn_type);
arkind = ctf_type_kind(lp->dn_ctfp, artype);

Expand All @@ -3525,8 +3538,8 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
dt_node_attr_assign(dnp, dt_attr_min(lp->dn_attr, rp->dn_attr));
dt_node_prop_alloca(dnp, lp, rp);

if (uref)
dnp->dn_flags |= DT_NF_USERLAND;
if (xflags)
dnp->dn_flags |= xflags;
break;
}

Expand Down Expand Up @@ -3649,11 +3662,11 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
if ((idp = dt_node_resolve(rp, DT_IDENT_XLSOU)) != NULL) {
ctfp = idp->di_ctfp;
type = idp->di_type;
uref = idp->di_flags & DT_IDFLG_USER;
xflags = idp->di_flags & DT_IDFLG_USER;
} else {
ctfp = rp->dn_ctfp;
type = rp->dn_type;
uref = rp->dn_flags & DT_NF_USERLAND;
xflags = rp->dn_flags & DT_NF_USERLAND;
}

/*
Expand All @@ -3679,7 +3692,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
dt_ident_type_assign(lp->dn_ident, ctfp, type);
dt_ident_set_storage(lp->dn_ident, alignment, size);

if (uref) {
if (xflags) {
lp->dn_flags |= DT_NF_USERLAND;
lp->dn_ident->di_flags |= DT_IDFLG_USER;
}
Expand Down Expand Up @@ -3861,11 +3874,11 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)

ctfp = idp->di_ctfp;
type = ctf_type_resolve(ctfp, idp->di_type);
uref = idp->di_flags & DT_IDFLG_USER;
xflags = idp->di_flags & DT_IDFLG_USER;
} else {
ctfp = lp->dn_ctfp;
type = ctf_type_resolve(ctfp, lp->dn_type);
uref = lp->dn_flags & DT_NF_USERLAND;
xflags = lp->dn_flags & DT_NF_USERLAND;
}

kind = ctf_type_kind(ctfp, type);
Expand Down Expand Up @@ -3936,7 +3949,7 @@ dt_cook_op2(dt_node_t *dnp, uint_t idflags)
if (lp->dn_flags & DT_NF_WRITABLE)
dnp->dn_flags |= DT_NF_WRITABLE;

if (uref && (kind == CTF_K_POINTER ||
if (xflags && (kind == CTF_K_POINTER ||
(dnp->dn_flags & DT_NF_REF)))
dnp->dn_flags |= DT_NF_USERLAND;
break;
Expand Down Expand Up @@ -4855,6 +4868,8 @@ dt_node_printr(dt_node_t *dnp, FILE *fp, int depth)
strcat(n, ",ALLOCA");
if (dnp->dn_flags & DT_NF_NONASSIGN)
strcat(n, ",NONASSIGN");
if (dnp->dn_flags & DT_NF_DPTR)
strcat(n, ",DPTR");
strcat(buf, n + 1);
} else
strcat(buf, "0");
Expand Down
1 change: 1 addition & 0 deletions libdtrace/dt_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ typedef struct dt_node {
#define DT_NF_USERLAND 0x40 /* data is a userland address */
#define DT_NF_ALLOCA 0x80 /* pointer derived from alloca() */
#define DT_NF_NONASSIGN 0x100 /* node is not assignable */
#define DT_NF_DPTR 0x200 /* node is a ptr to DTrace-managed storage */

#define DT_TYPE_NAMELEN 128 /* reasonable size for ctf_type_name() */

Expand Down
3 changes: 1 addition & 2 deletions test/unittest/arrays/tst.ctf-bounds.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 */

/*
* ASSERTION:
Expand Down
3 changes: 1 addition & 2 deletions test/unittest/arrays/tst.ctf-bounds.oob-cast.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 */

/*
* ASSERTION:
Expand Down
3 changes: 1 addition & 2 deletions test/unittest/arrays/tst.declared-bounds.oob-cast.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 */

/*
* ASSERTION:
Expand Down
3 changes: 1 addition & 2 deletions test/unittest/builtinvar/tst.hpriority.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 - uses reading from kernel addresses */

/*
* ASSERTION:
Expand Down
3 changes: 1 addition & 2 deletions test/unittest/builtinvar/tst.lwpsinfo.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 */

/*
* ASSERTION:
Expand Down
3 changes: 1 addition & 2 deletions test/unittest/builtinvar/tst.lwpsinfo1.d
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
/* @@xfail: dtv2 - uses reading from kernel addresses */

/*
* ASSERTION:
Expand Down
20 changes: 20 additions & 0 deletions test/unittest/codegen/tst.kernel_read_deref_indirect_scalar.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/

#pragma D option quiet

BEGIN
{
ptr = &`max_pfn;
trace(*ptr);
exit(0);
}

ERROR
{
exit(1);
}

0 comments on commit febf457

Please sign in to comment.