Skip to content

Commit

Permalink
Add support for copyoutstr() subroutine
Browse files Browse the repository at this point in the history
Move tests to their own copyoutstr/ subdirectory to mimic other copy*
tests.

Signed-off-by: Eugene Loh <eugene.loh@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
euloh authored and kvanhees committed Feb 26, 2023
1 parent d122722 commit 69d78b0
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 7 deletions.
75 changes: 74 additions & 1 deletion libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -4769,6 +4769,79 @@ dt_cg_subr_copyout(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
TRACE_REGSET(" subr-copyout:End ");
}

static void
dt_cg_subr_copyoutstr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
dt_node_t *src = dnp->dn_args;
dt_node_t *dst = src->dn_list;
dt_node_t *size = dst->dn_list;
size_t strsize = yypcb->pcb_hdl->dt_options[DTRACEOPT_STRSIZE];
uint64_t off;
uint_t L1 = dt_irlist_label(dlp);
uint_t L2 = dt_irlist_label(dlp);
uint_t L3 = dt_irlist_label(dlp);

dt_cg_clsflags(yypcb, DTRACEACT_PROC_DESTRUCTIVE, dnp);

TRACE_REGSET(" subr-copyoutstr:Begin");

dt_cg_node(src, dlp, drp);
dt_cg_node(dst, dlp, drp);
dt_cg_node(size, dlp, drp);

/* if (size > strsize) size = strsize */
emit(dlp, BPF_BRANCH_IMM(BPF_JLE, size->dn_reg, strsize, L1));
emit(dlp, BPF_MOV_IMM(size->dn_reg, strsize));
emitl(dlp, L1,
BPF_NOP());

/* validate the pointers */
dt_cg_check_ptr_arg(dlp, drp, src, size);
dt_cg_check_notnull(dlp, drp, dst->dn_reg);

/* copy into a temporary string to determine the string length */
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
emit(dlp, BPF_MOV_REG(BPF_REG_2, size->dn_reg));
dt_regset_free(drp, size->dn_reg);
emit(dlp, BPF_MOV_REG(BPF_REG_3, src->dn_reg));
off = dt_cg_tstring_xalloc(yypcb);
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_1, DCTX_MEM));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, off));
dt_regset_xalloc(drp, BPF_REG_0);
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_read_str));
dt_regset_free_args(drp);
dt_cg_tstring_xfree(yypcb, off);

/* check that we could read the string */
emit(dlp, BPF_BRANCH_IMM(BPF_JSGT, BPF_REG_0, 0, L2));
dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, src->dn_reg); // FIXME: is this right?
emitl(dlp, L2,
BPF_NOP());

/* copy with the measured size */
if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
emit(dlp, BPF_MOV_REG(BPF_REG_1, dst->dn_reg));
emit(dlp, BPF_MOV_REG(BPF_REG_2, src->dn_reg));
emit(dlp, BPF_MOV_REG(BPF_REG_3, BPF_REG_0));
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_probe_write_user));
dt_regset_free_args(drp);

/* since src was validated, any problem must be with dst */
emit(dlp, BPF_BRANCH_IMM(BPF_JEQ, BPF_REG_0, 0, L3));
dt_regset_free(drp, BPF_REG_0);
dt_cg_probe_error(yypcb, DTRACEFLT_BADADDR, DT_ISREG, dst->dn_reg);
emitl(dlp, L3,
BPF_NOP());

dt_regset_free(drp, src->dn_reg);
dt_regset_free(drp, dst->dn_reg);

TRACE_REGSET(" subr-copyoutstr:End ");
}

static void
dt_cg_subr_strchr(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
{
Expand Down Expand Up @@ -5246,7 +5319,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
[DIF_SUBR_PROGENYOF] = &dt_cg_subr_progenyof,
[DIF_SUBR_STRLEN] = &dt_cg_subr_strlen,
[DIF_SUBR_COPYOUT] = &dt_cg_subr_copyout,
[DIF_SUBR_COPYOUTSTR] = NULL,
[DIF_SUBR_COPYOUTSTR] = &dt_cg_subr_copyoutstr,
[DIF_SUBR_ALLOCA] = &dt_cg_subr_alloca,
[DIF_SUBR_BCOPY] = &dt_cg_subr_bcopy,
[DIF_SUBR_COPYINTO] = &dt_cg_subr_copyinto,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- @@stderr --
dtrace: failed to compile script test/unittest/funcs/copyoutstr/err.D_PROTO_LEN.copyoutstrbadarg.d: [D_PROTO_LEN] line 19: copyoutstr( ) prototype mismatch: 2 args passed, 3 expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- @@stderr --
dtrace: failed to compile script test/unittest/funcs/copyoutstr/err.D_PROTO_LEN.copyoutstrtoofew.d: [D_PROTO_LEN] line 19: copyoutstr( ) prototype mismatch: 1 arg passed, 3 expected
6 changes: 6 additions & 0 deletions test/unittest/funcs/copyoutstr/tst.copyoutstr.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
size is longer than the string; expect full string with terminating byte
HELLO WORLD; YOU HAVE A LONG MESSAGE TO DELIVER|---------------

size is shorter than the string; expect 32 chars without terminating byte
HELLO WORLD; YOU HAVE A LONG MES-------------------------------

73 changes: 73 additions & 0 deletions test/unittest/funcs/copyoutstr/tst.copyoutstr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/bin/bash
#
# Oracle Linux DTrace.
# Copyright (c) 2023, 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.

dtrace=$1
DIRNAME="$tmpdir/copyoutstr.$$.$RANDOM"
mkdir -p $DIRNAME
cd $DIRNAME

# Construct a C program that:
# - initializes a user buffer
# - passes that buffer to a syscall (where DTrace will overwrite the buffer)
# - checks the buffer
cat << EOF > main.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int c, char **v) {
int i, fd = open("/dev/null", O_WRONLY);
char s[64];
/* initialize buffer */
memset(s, '-', sizeof(s));
s[sizeof(s) - 1] = '\0';
/* write buffer to /dev/null (the D script will overwrite s) */
write(fd, s, strlen(s));
close(fd);
/* turn NUL chars (except last one) into printable '|' */
for (i = 0; i < sizeof(s) - 1; i++)
if (s[i] == '\0')
s[i] = '|';
/* print buffer contents */
printf("%s\n", s);
return 0;
}
EOF

gcc main.c
if [ $? -ne 0 ]; then
echo "compilation error"
exit 1
fi

# $1 is copyoutstr() size
# $2 is DTrace options
# $3 is description of test
function mytest() {
echo $3
$dtrace $dt_flags $2 -qwn '
syscall::write:entry
/pid == $target/
{
s = "HELLO WORLD; YOU HAVE A LONG MESSAGE TO DELIVER";
copyoutstr(s, arg1, '$1');
exit(0);
}' -c ./a.out
if [ $? -ne 0 ]; then
echo "DTrace error"
exit 1
fi
}

mytest 64 " " "size is longer than the string; expect full string with terminating byte"
mytest 32 " " "size is shorter than the string; expect 32 chars without terminating byte"

exit 0
2 changes: 0 additions & 2 deletions test/unittest/funcs/err.D_PROTO_LEN.copyoutstrbadarg.r

This file was deleted.

2 changes: 0 additions & 2 deletions test/unittest/funcs/err.D_PROTO_LEN.copyoutstrtoofew.r

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/usr/sbin/dtrace -ws
/*
* Oracle Linux DTrace.
* Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2023, 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.
*/
/* @@skip: dtv2, no copyoutstr yet */

/*
* ASSERTION: Destructive actions may never be speculative.
*
Expand Down

0 comments on commit 69d78b0

Please sign in to comment.