kasan: infer allocation size by scanning metadata
[linux-2.6-microblaze.git] / mm / kasan / report.c
index 22598b2..89078f9 100644 (file)
@@ -231,33 +231,46 @@ static inline struct page *addr_to_page(const void *addr)
        return NULL;
 }
 
-static void describe_object_addr(const void *addr, struct kmem_cache *cache,
-                                void *object)
+static void describe_object_addr(const void *addr, struct kasan_report_info *info)
 {
        unsigned long access_addr = (unsigned long)addr;
-       unsigned long object_addr = (unsigned long)object;
-       const char *rel_type;
+       unsigned long object_addr = (unsigned long)info->object;
+       const char *rel_type, *region_state = "";
        int rel_bytes;
 
        pr_err("The buggy address belongs to the object at %px\n"
               " which belongs to the cache %s of size %d\n",
-               object, cache->name, cache->object_size);
+               info->object, info->cache->name, info->cache->object_size);
 
        if (access_addr < object_addr) {
                rel_type = "to the left";
                rel_bytes = object_addr - access_addr;
-       } else if (access_addr >= object_addr + cache->object_size) {
+       } else if (access_addr >= object_addr + info->alloc_size) {
                rel_type = "to the right";
-               rel_bytes = access_addr - (object_addr + cache->object_size);
+               rel_bytes = access_addr - (object_addr + info->alloc_size);
        } else {
                rel_type = "inside";
                rel_bytes = access_addr - object_addr;
        }
 
+       /*
+        * Tag-Based modes use the stack ring to infer the bug type, but the
+        * memory region state description is generated based on the metadata.
+        * Thus, defining the region state as below can contradict the metadata.
+        * Fixing this requires further improvements, so only infer the state
+        * for the Generic mode.
+        */
+       if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {
+               if (strcmp(info->bug_type, "slab-out-of-bounds") == 0)
+                       region_state = "allocated ";
+               else if (strcmp(info->bug_type, "slab-use-after-free") == 0)
+                       region_state = "freed ";
+       }
+
        pr_err("The buggy address is located %d bytes %s of\n"
-              " %d-byte region [%px, %px)\n",
-               rel_bytes, rel_type, cache->object_size, (void *)object_addr,
-               (void *)(object_addr + cache->object_size));
+              " %s%zu-byte region [%px, %px)\n",
+              rel_bytes, rel_type, region_state, info->alloc_size,
+              (void *)object_addr, (void *)(object_addr + info->alloc_size));
 }
 
 static void describe_object_stacks(struct kasan_report_info *info)
@@ -279,7 +292,7 @@ static void describe_object(const void *addr, struct kasan_report_info *info)
 {
        if (kasan_stack_collection_enabled())
                describe_object_stacks(info);
-       describe_object_addr(addr, info->cache, info->object);
+       describe_object_addr(addr, info);
 }
 
 static inline bool kernel_or_module_addr(const void *addr)
@@ -436,6 +449,12 @@ static void complete_report_info(struct kasan_report_info *info)
        if (slab) {
                info->cache = slab->slab_cache;
                info->object = nearest_obj(info->cache, slab, addr);
+
+               /* Try to determine allocation size based on the metadata. */
+               info->alloc_size = kasan_get_alloc_size(info->object, info->cache);
+               /* Fallback to the object size if failed. */
+               if (!info->alloc_size)
+                       info->alloc_size = info->cache->object_size;
        } else
                info->cache = info->object = NULL;