]> sourceware.org Git - systemtap.git/commitdiff
stapbpf PR27030 WIP :: new bpf/uconversions.stp tapset
authorSerhei Makarov <smakarov@redhat.com>
Wed, 3 Feb 2021 15:36:33 +0000 (10:36 -0500)
committerSerhei Makarov <smakarov@redhat.com>
Wed, 3 Feb 2021 15:36:33 +0000 (10:36 -0500)
Tentative version of user_long_error() for bpf, needs more testing.
NEXT, the __bpf_probe_read_user_error() helper can be used to implement
the other user_{char,short,int} tapset functions.

This patch starts a new practice for tapset layout for tapset functions
that have a lkm/dyninst implementation and a separate bpf implementation:

- The lkm/dyninst implementation is placed in the toplevel tapset/
  directory, surrounded by %( runtime != "bpf" %? ... %).

- The bpf implementation is placed in the tapset/bpf/ directory.

Once applied to the rest of the tapsets, this practice should work to
eliminate the current proliferation of two-way
%( runtime != "bpf" %? implementation1 %: implementation2 %)
conditionals in the runtime code, and allow bpf versions of numerous
remaining tapset functions to be implemented and cleanly placed
into separate files under bpf/.

tapset/bpf/uconversions.stp [new file with mode: 0644]
tapset/uconversions.stp

diff --git a/tapset/bpf/uconversions.stp b/tapset/bpf/uconversions.stp
new file mode 100644 (file)
index 0000000..4c04327
--- /dev/null
@@ -0,0 +1,149 @@
+// uconversions tapset -- BPF version
+// Copyright (C) 2018-2020 Red Hat Inc.
+//
+// This file is part of systemtap, and is free software.  You can
+// redistribute it and/or modify it under the terms of the GNU General
+// Public License (GPL); either version 2, or (at your option) any
+// later version.
+
+function user_string_n:string (addr:long, n:long)
+{ /* pure */ /* bpf */
+  // TODO: Does not provide address in error message.
+  return user_string_n(addr, n, "user string copy fault")
+}
+
+function user_string_n:string (addr:long, n:long, err_msg:string)
+%( 1 == 1 %?
+{ /* pure */ /* bpf */
+  /* !!! ACHTUNG !!!
+   * bpf uses the same bpf_probe_read() helpers for kernel and user
+   * addresses, on the assumption that the address spaces coincide.
+   * Which only really works on x86_64 in Current Day.
+   *
+   * If the address space is changed, it may return the wrong data.
+   * TODO PR25168: Fix this as soon as BPF ships proper, separate
+   * bpf_probe_read_{user,kernel}() helpers.
+   */
+  %( arch == "x86_64" %?
+     // TODO: Does not use the provided err_msg.
+     return kernel_string_n(addr, n) // calls probe_read_str()
+  %:
+     // TODO: Use error() function.
+     print("ERROR(unsupported): %s", err_msg)
+     exit()
+  %)
+}
+%:
+%{ /* bpf */ /* pure */
+  /* if (n > BPF_MAXSTRINGLEN) n = BPF_MAXSTRINGLEN; */
+  0xb5, $n, -, _skip, BPF_MAXSTRINGLEN; /* jle $n, BPF_MAXSTRINGLEN, _skip */
+  0xb7, $n, -, -, BPF_MAXSTRINGLEN; /* mov $n, BPF_MAXSTRINGLEN */
+
+  label, _skip;
+  /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN);
+     buf[0] = 0x0; // guarantee NUL byte
+     rc = bpf_probe_read_user_str(buf, n, addr); */
+  alloc, $buf, BPF_MAXSTRINGLEN;
+  0x62, $buf, -, -, 0x0; /* stw [buf+0], 0 -- guarantee NUL byte */
+  call, $rc, probe_read_user_str, $buf, $n, $addr; /* TODO: should work if the helper is named bpf_probe_read_user_str() too */
+  /* TODO substitute probe_read_str automatically for older bpf? */
+
+  /* if (rc < 0) error("...", addr); */
+  0x75, $rc, -, _done, 0; /* jsge $rc, 0, _done */
+  call, -, printf, "ERROR: string copy fault at %p [man error::fault]", $addr; /* TODO document stapbpf version of error::fault */
+  call, -, printf, "%s", $err_msg; /* TODO */
+  call, -, exit;
+
+  label, _done;
+  /* return buf; */
+  0xbf, $$, $buf, -, -; /* mov $$, buf */
+%}
+%)
+
+function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
+%( 1 == 1 %?
+{ /* pure */ /* bpf */
+  /* !!! ACHTUNG !!!
+   * bpf uses the same bpf_probe_read() helpers for kernel and user
+   * addresses, on the assumption that the address spaces coincide.
+   * Which only really works on x86_64 in Current Day.
+   *
+   * If the address space is changed, it may return the wrong data.
+   * TODO PR25168: Fix this as soon as BPF ships proper, separate
+   * bpf_probe_read_{user,kernel}() helpers.
+   */
+  %( arch == "x86_64" %?
+     return kernel_string_n(addr, n, warn_msg) // calls probe_read_str()
+  %:
+     return warn_msg // don't even bother
+  %)
+}
+%:
+%{ /* bpf */ /* pure */
+  /* buf = bpf_stk_alloc(BPF_MAXSTRINGLEN);
+     buf[0] = 0x0; // guarantee NUL byte
+     rc = bpf_probe_read_user_str(buf, BPF_MAXSTRINGLEN, addr); */
+  alloc, $buf, BPF_MAXSTRINGLEN;
+  0x62, $buf, -, -, 0x0; /* stw [$buf+0], 0x0 -- guarantee NUL byte */
+  call, $rc, probe_read_user_str, $buf, BPF_MAXSTRINGLEN, $addr;
+
+  /* if (rc < 0) return err_msg;
+     return buf; */
+  0xc5, $rc, -, _err, 0; /* jslt $rc, 0, _err */
+  0xbf, $$, $buf, -, -; /* mov $$, $buf */
+  0x05, -, -, _done, -; /* ja _done; */
+
+  label, _err;
+  0xbf, $$, $warn_msg, -, -; /* mov $$, $warn_msg */
+
+  label, _done;
+%}
+%)
+
+function __bpf_probe_read_user_error:long (addr:long, size:long)
+%{ /* bpf */ /* pure */
+  /* if (size > 8) error("...", addr, size); */
+  0xb5, $size, -, _skip, 8; /* jle $n, 8, _skip */
+  call, -, printf, "ERROR: attempted to read %ul (>8) bytes from %p into unsigned long\n", $size, $addr;
+  call, -, exit;
+
+  label, _skip;
+  /* buf = bpf_stk_alloc(8); // size <= 8
+     *buf = (unsigned long)0x0; // guarantee leading zeroes
+     rc = bpf_probe_read(buf, size, addr);
+   */
+  alloc, $buf, 8;
+  0x7a, $buf, -, -, 0x0; /* stdw [buf+0], 0 -- guarantee leading zeroes */
+  /* TODO PR25168: probe_read is used for both kernel and user memory.
+     BPF will soon deprecate these in favour of separate functions. */
+  call, $rc, probe_read, $buf, $size, $addr;
+  /* call, $rc, probe_read_user, $buf, $size, $addr; */
+  /* XXX code for testing LITTLE ENDIAN / BIG ENDIAN */
+  /* 0xb7, $rc, -, -, 0x0;
+  0x62, $buf, -, -, 0xdeadbeef;
+  0x62, $buf, -, 4, 0xea7b3375; /* end test */
+
+  /* if (rc < 0) error("...", addr); */
+  0x75, $rc, -, _done, 0; /* jsge $rc, 0, _done */
+  call, -, printf, "ERROR: user copy fault at %p [man error::fault]\n", $addr; /* TODO document stapbpf version of error::fault */
+  call, -, exit;
+
+  label, _done;
+  /* return *(unsigned long *)buf >> 8*(8-size); // LITTLE ENDIAN */
+  0x79, $val, $buf, 0x0, -; /* ldxdw $val, buf */
+  0xb7, $shift, -, -, 0x8; /* mov $shift, 8 */
+  0x1f, $shift, $size, -, -; /* sub $shift, $size */
+  0x27, $shift, -, -, 0x8; /* mul $shift, 8 */
+  0x7f, $val, $shift, -, -; /* rsh $val, $shift */
+  0xbf, $$, $val, -, -; /* mov $$, $val */
+%}
+
+function user_long_error:long (addr:long) { /* pure */ /* bpf */
+  /* XXX code for testing LITTLE ENDIAN / BIG ENDIAN */
+  /* printf("1: %lx\n2: %x\n4: %lx\n8: %lx\n",
+    __bpf_probe_read_user_error(addr, 1),
+    __bpf_probe_read_user_error(addr, 2),
+    __bpf_probe_read_user_error(addr, 4),
+    __bpf_probe_read_user_error(addr, 8)) /* end test */
+  return __bpf_probe_read_user_error(addr, 8)
+}
index 34ea1861dedd5e378a226698eb66956c4e703fd5..f9cfc407d2b195b0b997f2b42c15f0d9b903f435 100644 (file)
@@ -1,5 +1,5 @@
 // userspace conversions tapset
-// Copyright (C) 2005-2019 Red Hat Inc.
+// Copyright (C) 2005-2021 Red Hat Inc.
 // Copyright (C) 2007 Intel Corporation.
 //
 // This file is part of systemtap, and is free software.  You can
@@ -123,8 +123,8 @@ function user_string_quoted:string (addr:long) {
  * given user space address. Reports an error on the rare cases
  * when userspace data is not accessible at the given address.
  */
-function user_string_n:string (addr:long, n:long)
 %( runtime != "bpf" %?
+function user_string_n:string (addr:long, n:long)
 %( systemtap_v < "2.3" %? // PR15044
        { return user_string_n(addr, n, "<unknown>") }
 %:
@@ -144,11 +144,6 @@ function user_string_n:string (addr:long, n:long)
                        STAP_RETVALUE[len - 1] = '\0';
        %}
 %)
-%:
-   { /* pure */ /* bpf */
-     // TODO: Does not provide address in error message.
-     return user_string_n(addr, n, "user string copy fault")
-   }
 %)
 
 /**
@@ -177,8 +172,8 @@ function user_string_n_nofault (addr:long, n:long) {
  * the rare cases when userspace data is not accessible at the given
  * address.
  */
-function user_string_n:string (addr:long, n:long, err_msg:string)
 %( runtime != "bpf" %?
+function user_string_n:string (addr:long, n:long, err_msg:string)
 %{ /* pure */ /* myproc-unprivileged */ /* unmodified-fnargs */
   int64_t len = clamp_t(int64_t, STAP_ARG_n + 1, 1, MAXSTRINGLEN - 1);
   if (_stp_strncpy_from_user(STAP_RETVALUE, 
@@ -187,26 +182,6 @@ function user_string_n:string (addr:long, n:long, err_msg:string)
   else
     STAP_RETVALUE[len - 1] = '\0';
 %}
-%:
-   { /* pure */ /* bpf */
-     /* !!! ACHTUNG !!!
-      * bpf uses the same bpf_probe_read() helpers for kernel and user
-      * addresses, on the assumption that the address spaces coincide.
-      * Which only really works on x86_64 in Current Day.
-      *
-      * If the address space is changed, it may return the wrong data.
-      * TODO PR25168: Fix this as soon as BPF ships proper, separate
-      * bpf_probe_read_{user,kernel}() helpers.
-      */
-     %( arch == "x86_64" %?
-        // TODO: Does not use the provided err_msg.
-        return kernel_string_n(addr, n) // calls probe_read_str()
-     %:
-        // TODO: Use error() function.
-        print("ERROR(unsupported): %s", err_msg)
-        exit()
-     %)
-   }
 %)
 function user_string_n2:string (addr:long, n:long, err_msg:string) {
   return user_string_n(addr, n, err_msg);
@@ -243,8 +218,8 @@ function user_string_n_warn:string (addr:long, n:long) {
  * rare cases when userspace data is not accessible and warns (but does
  * not abort) about the failure.
  */
-function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
 %( runtime != "bpf" %?
+function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
 %{ /* pure */ /* myproc-unprivileged */ /* unmodified-fnargs */
        int64_t len = clamp_t(int64_t, STAP_ARG_n + 1, 1, MAXSTRINGLEN - 1);
        long rc;
@@ -261,23 +236,6 @@ function user_string_n_warn:string (addr:long, n:long, warn_msg:string)
        } else
                STAP_RETVALUE[len - 1] = '\0';
 %}
-%:
-   { /* pure */ /* bpf */
-     /* !!! ACHTUNG !!!
-      * bpf uses the same bpf_probe_read() helpers for kernel and user
-      * addresses, on the assumption that the address spaces coincide.
-      * Which only really works on x86_64 in Current Day.
-      *
-      * If the address space is changed, it may return the wrong data.
-      * TODO PR25168: Fix this as soon as BPF ships proper, separate
-      * bpf_probe_read_{user,kernel}() helpers.
-      */
-     %( arch == "x86_64" %?
-        return kernel_string_n(addr, n, warn_msg) // calls probe_read_str()
-     %:
-        return warn_msg // don't even bother
-     %)
-   }
 %)
 function user_string2_n_warn:string (addr:long, n:long, warn_msg:string) {
   user_string_n_warn(addr, n, warn_msg);
@@ -716,7 +674,9 @@ function user_int_warn:long (addr:long)
  * of the current user space task (for those architectures that
  * support both 64/32 bit compat tasks).
  */
-function user_long_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged */
+%( runtime != "bpf" %?
+function user_long_error:long (addr:long)
+%{ /* pure */ /* myproc-unprivileged */
 #ifdef CONFIG_COMPAT
        if (_stp_is_compat_task())
                STP_GET_USER(compat_long_t);
@@ -725,6 +685,7 @@ function user_long_error:long (addr:long) %{ /* pure */ /* myproc-unprivileged *
                STP_GET_USER(long);
        CATCH_DEREF_FAULT();
 %}
+%)
 
 /**
  * sfunction user_long - Retrieves a long value stored in user space
This page took 0.034687 seconds and 5 git commands to generate.