tunnels: fix netns vs proto registration ordering
[safe/jmp/linux-2.6] / mm / kmemleak.c
index f06c092..5b069e4 100644 (file)
@@ -93,6 +93,7 @@
 #include <linux/nodemask.h>
 #include <linux/mm.h>
 #include <linux/workqueue.h>
+#include <linux/crc32.h>
 
 #include <asm/sections.h>
 #include <asm/processor.h>
 #define MSECS_MIN_AGE          5000    /* minimum object age for reporting */
 #define SECS_FIRST_SCAN                60      /* delay before the first scan */
 #define SECS_SCAN_WAIT         600     /* subsequent auto scanning delay */
-#define GRAY_LIST_PASSES       25      /* maximum number of gray list scans */
 #define MAX_SCAN_SIZE          4096    /* maximum size of a scanned block */
 
 #define BYTES_PER_POINTER      sizeof(void *)
@@ -149,6 +149,8 @@ struct kmemleak_object {
        int min_count;
        /* the total number of pointers found pointing to this object */
        int count;
+       /* checksum for detecting modified objects */
+       u32 checksum;
        /* memory ranges to be scanned inside an object (empty for all) */
        struct hlist_head area_list;
        unsigned long trace[MAX_TRACE];
@@ -164,8 +166,6 @@ struct kmemleak_object {
 #define OBJECT_REPORTED                (1 << 1)
 /* flag set to not scan the object */
 #define OBJECT_NO_SCAN         (1 << 2)
-/* flag set on newly allocated objects */
-#define OBJECT_NEW             (1 << 3)
 
 /* number of bytes to print per line; must be 16 or 32 */
 #define HEX_ROW_SIZE           16
@@ -321,11 +321,6 @@ static bool color_gray(const struct kmemleak_object *object)
                object->count >= object->min_count;
 }
 
-static bool color_black(const struct kmemleak_object *object)
-{
-       return object->min_count == KMEMLEAK_BLACK;
-}
-
 /*
  * Objects are considered unreferenced only if their color is white, they have
  * not be deleted and have a minimum age to avoid false positives caused by
@@ -333,7 +328,7 @@ static bool color_black(const struct kmemleak_object *object)
  */
 static bool unreferenced_object(struct kmemleak_object *object)
 {
-       return (object->flags & OBJECT_ALLOCATED) && color_white(object) &&
+       return (color_white(object) && object->flags & OBJECT_ALLOCATED) &&
                time_before_eq(object->jiffies + jiffies_min_age,
                               jiffies_last_scan);
 }
@@ -346,11 +341,13 @@ static void print_unreferenced(struct seq_file *seq,
                               struct kmemleak_object *object)
 {
        int i;
+       unsigned int msecs_age = jiffies_to_msecs(jiffies - object->jiffies);
 
        seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
                   object->pointer, object->size);
-       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
-                  object->comm, object->pid, object->jiffies);
+       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
+                  object->comm, object->pid, object->jiffies,
+                  msecs_age / 1000, msecs_age % 1000);
        hex_dump_object(seq, object);
        seq_printf(seq, "  backtrace:\n");
 
@@ -379,6 +376,7 @@ static void dump_object_info(struct kmemleak_object *object)
        pr_notice("  min_count = %d\n", object->min_count);
        pr_notice("  count = %d\n", object->count);
        pr_notice("  flags = 0x%lx\n", object->flags);
+       pr_notice("  checksum = %d\n", object->checksum);
        pr_notice("  backtrace:\n");
        print_stack_trace(&trace, 4);
 }
@@ -520,12 +518,13 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,
        INIT_HLIST_HEAD(&object->area_list);
        spin_lock_init(&object->lock);
        atomic_set(&object->use_count, 1);
-       object->flags = OBJECT_ALLOCATED | OBJECT_NEW;
+       object->flags = OBJECT_ALLOCATED;
        object->pointer = ptr;
        object->size = size;
        object->min_count = min_count;
-       object->count = -1;                     /* no color initially */
+       object->count = 0;                      /* white color initially */
        object->jiffies = jiffies;
+       object->checksum = 0;
 
        /* task information */
        if (in_irq()) {
@@ -947,6 +946,20 @@ void __ref kmemleak_no_scan(const void *ptr)
 EXPORT_SYMBOL(kmemleak_no_scan);
 
 /*
+ * Update an object's checksum and return true if it was modified.
+ */
+static bool update_checksum(struct kmemleak_object *object)
+{
+       u32 old_csum = object->checksum;
+
+       if (!kmemcheck_is_obj_initialized(object->pointer, object->size))
+               return false;
+
+       object->checksum = crc32(0, (void *)object->pointer, object->size);
+       return object->checksum != old_csum;
+}
+
+/*
  * Memory scanning is a long process and it needs to be interruptable. This
  * function checks whether such interrupt condition occured.
  */
@@ -1047,8 +1060,8 @@ static void scan_object(struct kmemleak_object *object)
        unsigned long flags;
 
        /*
-        * Once the object->lock is aquired, the corresponding memory block
-        * cannot be freed (the same lock is aquired in delete_object).
+        * Once the object->lock is acquired, the corresponding memory block
+        * cannot be freed (the same lock is acquired in delete_object).
         */
        spin_lock_irqsave(&object->lock, flags);
        if (object->flags & OBJECT_NO_SCAN)
@@ -1080,6 +1093,39 @@ out:
 }
 
 /*
+ * Scan the objects already referenced (gray objects). More objects will be
+ * referenced and, if there are no memory leaks, all the objects are scanned.
+ */
+static void scan_gray_list(void)
+{
+       struct kmemleak_object *object, *tmp;
+
+       /*
+        * The list traversal is safe for both tail additions and removals
+        * from inside the loop. The kmemleak objects cannot be freed from
+        * outside the loop because their use_count was incremented.
+        */
+       object = list_entry(gray_list.next, typeof(*object), gray_list);
+       while (&object->gray_list != &gray_list) {
+               cond_resched();
+
+               /* may add new objects to the list */
+               if (!scan_should_stop())
+                       scan_object(object);
+
+               tmp = list_entry(object->gray_list.next, typeof(*object),
+                                gray_list);
+
+               /* remove the object from the list and release it */
+               list_del(&object->gray_list);
+               put_object(object);
+
+               object = tmp;
+       }
+       WARN_ON(!list_empty(&gray_list));
+}
+
+/*
  * Scan data sections and all the referenced memory blocks allocated via the
  * kernel's standard allocators. This function must be called with the
  * scan_mutex held.
@@ -1087,10 +1133,9 @@ out:
 static void kmemleak_scan(void)
 {
        unsigned long flags;
-       struct kmemleak_object *object, *tmp;
+       struct kmemleak_object *object;
        int i;
        int new_leaks = 0;
-       int gray_list_pass = 0;
 
        jiffies_last_scan = jiffies;
 
@@ -1111,7 +1156,6 @@ static void kmemleak_scan(void)
 #endif
                /* reset the reference count (whiten the object) */
                object->count = 0;
-               object->flags &= ~OBJECT_NEW;
                if (color_gray(object) && get_object(object))
                        list_add_tail(&object->gray_list, &gray_list);
 
@@ -1169,62 +1213,36 @@ static void kmemleak_scan(void)
 
        /*
         * Scan the objects already referenced from the sections scanned
-        * above. More objects will be referenced and, if there are no memory
-        * leaks, all the objects will be scanned. The list traversal is safe
-        * for both tail additions and removals from inside the loop. The
-        * kmemleak objects cannot be freed from outside the loop because their
-        * use_count was increased.
+        * above.
         */
-repeat:
-       object = list_entry(gray_list.next, typeof(*object), gray_list);
-       while (&object->gray_list != &gray_list) {
-               cond_resched();
-
-               /* may add new objects to the list */
-               if (!scan_should_stop())
-                       scan_object(object);
-
-               tmp = list_entry(object->gray_list.next, typeof(*object),
-                                gray_list);
-
-               /* remove the object from the list and release it */
-               list_del(&object->gray_list);
-               put_object(object);
-
-               object = tmp;
-       }
-
-       if (scan_should_stop() || ++gray_list_pass >= GRAY_LIST_PASSES)
-               goto scan_end;
+       scan_gray_list();
 
        /*
-        * Check for new objects allocated during this scanning and add them
-        * to the gray list.
+        * Check for new or unreferenced objects modified since the previous
+        * scan and color them gray until the next scan.
         */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
                spin_lock_irqsave(&object->lock, flags);
-               if ((object->flags & OBJECT_NEW) && !color_black(object) &&
-                   get_object(object)) {
-                       object->flags &= ~OBJECT_NEW;
+               if (color_white(object) && (object->flags & OBJECT_ALLOCATED)
+                   && update_checksum(object) && get_object(object)) {
+                       /* color it gray temporarily */
+                       object->count = object->min_count;
                        list_add_tail(&object->gray_list, &gray_list);
                }
                spin_unlock_irqrestore(&object->lock, flags);
        }
        rcu_read_unlock();
 
-       if (!list_empty(&gray_list))
-               goto repeat;
-
-scan_end:
-       WARN_ON(!list_empty(&gray_list));
+       /*
+        * Re-scan the gray list for modified unreferenced objects.
+        */
+       scan_gray_list();
 
        /*
-        * If scanning was stopped or new objects were being allocated at a
-        * higher rate than gray list scanning, do not report any new
-        * unreferenced objects.
+        * If scanning was stopped do not report any new unreferenced objects.
         */
-       if (scan_should_stop() || gray_list_pass >= GRAY_LIST_PASSES)
+       if (scan_should_stop())
                return;
 
        /*