[PATCH 3/4] Implement code to read coverage informations

Frediano Ziglio
Fri, 01 Feb 2013 14:48:22 -0000

Implement 4 operations
- check if coverage is compiled in
- read size of coverage information
- read coverage information
- reset coverage counters

Information are stored in a single blob in a format specific to Xen designed
to make code that generate coverage as small as possible.

Check does not check for any permission while other operations require a
privileged domain. This cause these statistics can lead to security
problems.

Signed-off-by: Frediano Ziglio <frediano.ziglio@citrix.com>
---
 xen/common/gcov/gcov.c    |  157 +++++++++++++++++++++++++++++++++++++++++++--
 xen/include/public/gcov.h |   24 +++++++
 2 files changed, 177 insertions(+), 4 deletions(-)

diff --git a/xen/common/gcov/gcov.c b/xen/common/gcov/gcov.c
index e208596..d5d74d0 100644
--- a/xen/common/gcov/gcov.c
+++ b/xen/common/gcov/gcov.c
@@ -19,10 +19,10 @@
 #include <xen/hypercall.h>
 #include <xen/gcov.h>
 #include <xen/errno.h>
+#include <xen/guest_access.h>
 #include <public/xen.h>

 static struct gcov_info *info_list;
-static unsigned num_info = 0;

 /*
  * __gcov_init is called by gcc-generated constructor code for each object
@@ -37,7 +37,6 @@ void __gcov_init(struct gcov_info *info)
     /* add new profiling data structure to list */
     info->next = info_list;
     info_list = info;
-    ++num_info;
 }

 /*
@@ -78,15 +77,165 @@ void init_coverage(void)
         __CTOR_LIST__.funcs[n]();

 #ifndef NDEBUG
-    printk(XENLOG_INFO "Initialized %u coverage strucures\n",
num_info);
     if ( info_list )
         printk(XENLOG_INFO "First coverage file %s\n",
info_list->filename);
 #endif
 }

+static inline int counter_active(const struct gcov_info *info, unsigned int
type)
+{
+    return (1 << type) & info->ctr_mask;
+}
+
+DEFINE_XEN_GUEST_HANDLE(uint8_t);
+
+typedef struct write_iter_t
+{
+    XEN_GUEST_HANDLE(uint8_t) ptr;
+    int real;
+    unsigned write_offset;
+} write_iter_t;
+
+static int write_raw(struct write_iter_t *iter, const void *data,
+                     size_t data_len)
+{
+    if ( iter->real &&
+        copy_to_guest_offset(iter->ptr, iter->write_offset,
+                             (const unsigned char *) data, data_len) )
+        return -EFAULT;
+
+    iter->write_offset +        (iter->write_offset + data_len +
(sizeof(uint32_t) - 1)) &
+        -sizeof(uint32_t);
+    return 0;
+}
+
+#define chk(v) do { ret=(v); if ( ret ) return ret; } while(0)
+
+static inline int write32(write_iter_t *iter, uint32_t val)
+{
+    return write_raw(iter, &val, sizeof(val));
+}
+
+static int write_string(write_iter_t *iter, const char *s)
+{
+    int ret;
+    size_t len = strlen(s);
+
+    chk(write32(iter, len));
+    return write_raw(iter, s, len);
+}
+
+static inline int next_type(const struct gcov_info *info, int *type)
+{
+    while ( ++*type < GCOV_COUNTERS && !counter_active(info, *type)
)
+        continue;
+    return *type;
+}
+
+static int write_gcov(write_iter_t *iter)
+{
+    struct gcov_info *info;
+    int ret;
+
+    /* reset offset */
+    iter->write_offset = 0;
+
+    /* dump all files */
+    for ( info = info_list ; info; info = info->next )
+    {
+        const struct gcov_ctr_info *ctr;
+        int type;
+        size_t size_fn = sizeof(struct gcov_fn_info);
+
+        chk(write32(iter, GCOV_DATA_MAGIC));
+        chk(write32(iter, info->version));
+        chk(write32(iter, info->stamp));
+        chk(write_string(iter, info->filename));
+
+        /* dump counters */
+        ctr = info->counts;
+        for ( type = -1; next_type(info, &type) < GCOV_COUNTERS; ++ctr )
+        {
+            chk(write32(iter, GCOV_TAG_FOR_COUNTER(type)));
+            chk(write32(iter, ctr->num));
+            chk(write_raw(iter, ctr->values,
+                          ctr->num * sizeof(ctr->values[0])));
+
+            size_fn += sizeof(unsigned);
+        }
+
+        /* dump all functions together */
+        chk(write32(iter, GCOV_TAG_FUNCTION));
+        chk(write32(iter, info->n_functions));
+        chk(write_raw(iter, info->functions, info->n_functions *
size_fn));
+    }
+
+    /* stop tag */
+    chk(write32(iter, 0));
+    return 0;
+}
+
+static int reset_counters(void)
+{
+    struct gcov_info *info;
+
+    for ( info = info_list ; info; info = info->next )
+    {
+        const struct gcov_ctr_info *ctr;
+        int type;
+
+        /* reset counters */
+        ctr = info->counts;
+        for ( type = -1; next_type(info, &type) < GCOV_COUNTERS; ++ctr )
+            memset(ctr->values, 0, ctr->num * sizeof(ctr->values[0]));
+    }
+
+    return 0;
+}
+
 long do_coverage_op(int op, XEN_GUEST_HANDLE_PARAM(void) uarg)
 {
-    return -EINVAL;
+    long ret = -EINVAL;
+    uint32_t len;
+    write_iter_t iter;
+
+    /*
+     * just return success to check for coverage support,
+     * no permission check
+     */
+    if ( op == XEN_COVERAGE_enabled )
+        return 0;
+
+    /*
+     * all other information although statistical one
+     * are too accurate and could lead to security issues
+     */
+    if ( !IS_PRIV(current->domain) )
+        return -EPERM;
+
+    switch ( op )
+    {
+    case XEN_COVERAGE_get_total_size:
+        iter.real = 0;
+
+        write_gcov(&iter);
+        len = iter.write_offset;
+        ret = copy_to_guest(uarg, &len, 1) ? -EFAULT : 0;
+        break;
+
+    case XEN_COVERAGE_read:
+        iter.ptr = guest_handle_cast(uarg, uint8_t);
+        iter.real = 1;
+
+        ret = write_gcov(&iter);
+        break;
+
+    case XEN_COVERAGE_reset:
+        ret = reset_counters();
+        break;
+    }
+    return ret;
 }

 #ifdef CONFIG_COMPAT
diff --git a/xen/include/public/gcov.h b/xen/include/public/gcov.h
index 67aedf6..a2863f7 100644
--- a/xen/include/public/gcov.h
+++ b/xen/include/public/gcov.h
@@ -90,4 +90,28 @@ struct gcov_info
     struct gcov_ctr_info      counts[0];
 };

+
+/*
+ * Check if coverage informations are available
+ * return just success or error, no parameters
+ */
+#define XEN_COVERAGE_enabled        0
+
+/*
+ * Get total size of information, to help allocate
+ * the buffer. The pointer points to a 32 bit value.
+ */
+#define XEN_COVERAGE_get_total_size 1
+
+/*
+ * Read coverage information in a single run
+ * You must use a tool to split them
+ */
+#define XEN_COVERAGE_read           2
+
+/*
+ * Reset all the coverage counters to 0
+ */
+#define XEN_COVERAGE_reset          3
+
 #endif /* XEN_PUBLIC_GCOV_H */
--
1.7.9.5