Fix: ensure userspace accesses are done with _inatomic
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 22 Aug 2012 20:30:20 +0000 (16:30 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Wed, 22 Aug 2012 20:30:20 +0000 (16:30 -0400)
Otherwise, triggers scheduling while atomic (might_sleep()) warnings,
since we call those from a tracepoint probe (with preemption disabled).

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Makefile
lib/ringbuffer/backend.h
lib/ringbuffer/backend_internal.h
lib/ringbuffer/ring_buffer_backend.c
lttng-ring-buffer-client.h
lttng-ring-buffer-metadata-client.h
probes/lttng-events.h
probes/lttng-probe-user.c [new file with mode: 0644]
probes/lttng-probe-user.h [new file with mode: 0644]

index d06e6764119d4acd0d49745be0ac8a2c4445b5b2..b91113e475e994b51b2c3aa9998fcccb425d3e45 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@ obj-m += lttng-statedump.o
 lttng-statedump-objs := lttng-statedump-impl.o wrapper/irqdesc.o
 
 ifneq ($(CONFIG_HAVE_SYSCALL_TRACEPOINTS),)
-lttng-tracer-objs += lttng-syscalls.o
+lttng-tracer-objs += lttng-syscalls.o probes/lttng-probe-user.o
 endif
 
 ifneq ($(CONFIG_PERF_EVENTS),)
index 2ce6ce9cd752508484123d4997d0f18481913b85..826be933fe61bb45efbe96a7738823e3ae46af9e 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/list.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/uaccess.h>
 
 /* Internal helpers */
 #include "../../wrapper/ringbuffer/backend_internal.h"
@@ -161,7 +162,7 @@ void lib_ring_buffer_memset(const struct lib_ring_buffer_config *config,
 }
 
 /**
- * lib_ring_buffer_copy_from_user - write userspace data to a buffer backend
+ * lib_ring_buffer_copy_from_user_inatomic - write userspace data to a buffer backend
  * @config : ring buffer instance configuration
  * @ctx: ring buffer context. (input arguments only)
  * @src : userspace source pointer to copy from
@@ -170,10 +171,11 @@ void lib_ring_buffer_memset(const struct lib_ring_buffer_config *config,
  * This function copies "len" bytes of data from a userspace pointer to a
  * buffer backend, at the current context offset. This is more or less a buffer
  * backend-specific memcpy() operation. Calls the slow path
- * (_ring_buffer_write_from_user) if copy is crossing a page boundary.
+ * (_ring_buffer_write_from_user_inatomic) if copy is crossing a page boundary.
+ * Disable the page fault handler to ensure we never try to take the mmap_sem.
  */
 static inline
-void lib_ring_buffer_copy_from_user(const struct lib_ring_buffer_config *config,
+void lib_ring_buffer_copy_from_user_inatomic(const struct lib_ring_buffer_config *config,
                                    struct lib_ring_buffer_ctx *ctx,
                                    const void __user *src, size_t len)
 {
@@ -185,6 +187,7 @@ void lib_ring_buffer_copy_from_user(const struct lib_ring_buffer_config *config,
        struct lib_ring_buffer_backend_pages *rpages;
        unsigned long sb_bindex, id;
        unsigned long ret;
+       mm_segment_t old_fs = get_fs();
 
        offset &= chanb->buf_size - 1;
        sbidx = offset >> chanb->subbuf_size_order;
@@ -197,11 +200,13 @@ void lib_ring_buffer_copy_from_user(const struct lib_ring_buffer_config *config,
                     config->mode == RING_BUFFER_OVERWRITE
                     && subbuffer_id_is_noref(config, id));
 
+       set_fs(KERNEL_DS);
+       pagefault_disable();
        if (unlikely(!access_ok(VERIFY_READ, src, len)))
                goto fill_buffer;
 
        if (likely(pagecpy == len)) {
-               ret = lib_ring_buffer_do_copy_from_user(
+               ret = lib_ring_buffer_do_copy_from_user_inatomic(
                        rpages->p[index].virt + (offset & ~PAGE_MASK),
                        src, len);
                if (unlikely(ret > 0)) {
@@ -210,13 +215,17 @@ void lib_ring_buffer_copy_from_user(const struct lib_ring_buffer_config *config,
                        goto fill_buffer;
                }
        } else {
-               _lib_ring_buffer_copy_from_user(bufb, offset, src, len, 0);
+               _lib_ring_buffer_copy_from_user_inatomic(bufb, offset, src, len, 0);
        }
+       pagefault_enable();
+       set_fs(old_fs);
        ctx->buf_offset += len;
 
        return;
 
 fill_buffer:
+       pagefault_enable();
+       set_fs(old_fs);
        /*
         * In the error path we call the slow path version to avoid
         * the pollution of static inline code.
index 3e262c2e27f65ca7b8b3ea5ca50fdbb8a7b92824..e048365ec44578104a919294916aeeb1472ca432 100644 (file)
@@ -56,7 +56,7 @@ extern void _lib_ring_buffer_write(struct lib_ring_buffer_backend *bufb,
 extern void _lib_ring_buffer_memset(struct lib_ring_buffer_backend *bufb,
                                    size_t offset, int c, size_t len,
                                    ssize_t pagecpy);
-extern void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
+extern void _lib_ring_buffer_copy_from_user_inatomic(struct lib_ring_buffer_backend *bufb,
                                            size_t offset, const void *src,
                                            size_t len, ssize_t pagecpy);
 
@@ -434,15 +434,15 @@ do {                                                              \
 } while (0)
 
 /*
- * We use __copy_from_user to copy userspace data since we already
+ * We use __copy_from_user_inatomic to copy userspace data since we already
  * did the access_ok for the whole range.
  */
 static inline
-unsigned long lib_ring_buffer_do_copy_from_user(void *dest,
+unsigned long lib_ring_buffer_do_copy_from_user_inatomic(void *dest,
                                                const void __user *src,
                                                unsigned long len)
 {
-       return __copy_from_user(dest, src, len);
+       return __copy_from_user_inatomic(dest, src, len);
 }
 
 /*
index e8ad4c521cfb66440e51eb4ae9ca0930ea6528d6..84e7dfb715c6ea1453478f6f85cd5dc8df54170c 100644 (file)
@@ -559,7 +559,7 @@ EXPORT_SYMBOL_GPL(_lib_ring_buffer_memset);
 
 
 /**
- * lib_ring_buffer_copy_from_user - write user data to a ring_buffer buffer.
+ * lib_ring_buffer_copy_from_user_inatomic - write user data to a ring_buffer buffer.
  * @bufb : buffer backend
  * @offset : offset within the buffer
  * @src : source address
@@ -570,7 +570,7 @@ EXPORT_SYMBOL_GPL(_lib_ring_buffer_memset);
  * directly without having the src pointer checked with access_ok()
  * previously.
  */
-void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
+void _lib_ring_buffer_copy_from_user_inatomic(struct lib_ring_buffer_backend *bufb,
                                      size_t offset,
                                      const void __user *src, size_t len,
                                      ssize_t pagecpy)
@@ -601,7 +601,7 @@ void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
                rpages = bufb->array[sb_bindex];
                CHAN_WARN_ON(chanb, config->mode == RING_BUFFER_OVERWRITE
                                && subbuffer_id_is_noref(config, id));
-               ret = lib_ring_buffer_do_copy_from_user(rpages->p[index].virt
+               ret = lib_ring_buffer_do_copy_from_user_inatomic(rpages->p[index].virt
                                                        + (offset & ~PAGE_MASK),
                                                        src, pagecpy) != 0;
                if (ret > 0) {
@@ -612,7 +612,7 @@ void _lib_ring_buffer_copy_from_user(struct lib_ring_buffer_backend *bufb,
                }
        } while (unlikely(len != pagecpy));
 }
-EXPORT_SYMBOL_GPL(_lib_ring_buffer_copy_from_user);
+EXPORT_SYMBOL_GPL(_lib_ring_buffer_copy_from_user_inatomic);
 
 /**
  * lib_ring_buffer_read - read data from ring_buffer_buffer.
index 9016b438746bd3ecad15351c6e4b6b4d1f2cac2f..cf2645a5912361ba23ec206114690f315ddba46c 100644 (file)
@@ -513,7 +513,7 @@ static
 void lttng_event_write_from_user(struct lib_ring_buffer_ctx *ctx,
                               const void __user *src, size_t len)
 {
-       lib_ring_buffer_copy_from_user(&client_config, ctx, src, len);
+       lib_ring_buffer_copy_from_user_inatomic(&client_config, ctx, src, len);
 }
 
 static
index 8660d997c322f23b41acb152ffc622c009aae1d4..1c77f99cd3177ec81908561110b9fbe4f4ab872f 100644 (file)
@@ -240,7 +240,7 @@ static
 void lttng_event_write_from_user(struct lib_ring_buffer_ctx *ctx,
                               const void __user *src, size_t len)
 {
-       lib_ring_buffer_copy_from_user(&client_config, ctx, src, len);
+       lib_ring_buffer_copy_from_user_inatomic(&client_config, ctx, src, len);
 }
 
 static
index d3d75ad518d82da5148d9971f71b596597c5a3ec..33cabcf9c87bd886e6381adad21ae6bbfb39dcde 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/debugfs.h>
 #include "lttng.h"
 #include "lttng-types.h"
+#include "lttng-probe-user.h"
 #include "../wrapper/vmalloc.h"        /* for wrapper_vmalloc_sync_all() */
 #include "../wrapper/ringbuffer/frontend_types.h"
 #include "../lttng-events.h"
@@ -359,7 +360,7 @@ static __used struct lttng_probe_desc TP_ID(__probe_desc___, TRACE_SYSTEM) = {
 #undef __string_from_user
 #define __string_from_user(_item, _src)                                               \
        __event_len += __dynamic_len[__dynamic_len_idx++] =                    \
-               max_t(size_t, strlen_user(_src), 1);
+               max_t(size_t, lttng_strlen_user_inatomic(_src), 1);
 
 #undef TP_PROTO
 #define TP_PROTO(args...) args
diff --git a/probes/lttng-probe-user.c b/probes/lttng-probe-user.c
new file mode 100644 (file)
index 0000000..94ecf2f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * lttng-probe-user.c
+ *
+ * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/uaccess.h>
+#include "lttng-probe-user.h"
+
+/*
+ * Calculate string length. Include final null terminating character if there is
+ * one, or ends at first fault. Disabling page faults ensures that we can safely
+ * call this from pretty much any context, including those where the caller
+ * holds mmap_sem, or any lock which nests in mmap_sem.
+ */
+long lttng_strlen_user_inatomic(const char *addr)
+{
+       long count = 0;
+       mm_segment_t old_fs = get_fs();
+
+       set_fs(KERNEL_DS);
+       pagefault_disable();
+       for (;;) {
+               char v;
+               long ret;
+
+               ret = __copy_from_user_inatomic(&v,
+                       (__force const char __user *)(addr),
+                       sizeof(v));
+               if (unlikely(ret == -EFAULT))
+                       break;
+               count++;
+               if (unlikely(!v))
+                       break;
+               addr++;
+       }
+       pagefault_enable();
+       set_fs(old_fs);
+       return count;
+}
diff --git a/probes/lttng-probe-user.h b/probes/lttng-probe-user.h
new file mode 100644 (file)
index 0000000..e03e4b0
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _LTTNG_PROBE_USER_H
+#define _LTTNG_PROBE_USER_H
+
+/*
+ * lttng-probe-user.h
+ *
+ * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; only
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Calculate string length. Include final null terminating character if there is
+ * one, or ends at first fault.
+ */
+long lttng_strlen_user_inatomic(const char *addr);
+
+#endif /* _LTTNG_PROBE_USER_H */
This page took 0.034258 seconds and 4 git commands to generate.