xref: /linux/mm/kasan/report_tags.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4  * Copyright (c) 2020 Google, Inc.
5  */
6 
7 #include <linux/atomic.h>
8 
9 #include "kasan.h"
10 #include "../slab.h"
11 
12 extern struct kasan_stack_ring stack_ring;
13 
get_common_bug_type(struct kasan_report_info * info)14 static const char *get_common_bug_type(struct kasan_report_info *info)
15 {
16 	/*
17 	 * If access_size is a negative number, then it has reason to be
18 	 * defined as out-of-bounds bug type.
19 	 *
20 	 * Casting negative numbers to size_t would indeed turn up as
21 	 * a large size_t and its value will be larger than ULONG_MAX/2,
22 	 * so that this can qualify as out-of-bounds.
23 	 */
24 	if (info->access_addr + info->access_size < info->access_addr)
25 		return "out-of-bounds";
26 
27 	return "invalid-access";
28 }
29 
kasan_complete_mode_report_info(struct kasan_report_info * info)30 void kasan_complete_mode_report_info(struct kasan_report_info *info)
31 {
32 	unsigned long flags;
33 	u64 pos;
34 	struct kasan_stack_ring_entry *entry;
35 	bool alloc_found = false, free_found = false;
36 
37 	if ((!info->cache || !info->object) && !info->bug_type) {
38 		info->bug_type = get_common_bug_type(info);
39 		return;
40 	}
41 
42 	write_lock_irqsave(&stack_ring.lock, flags);
43 
44 	pos = atomic64_read(&stack_ring.pos);
45 
46 	/*
47 	 * The loop below tries to find stack ring entries relevant to the
48 	 * buggy object. This is a best-effort process.
49 	 *
50 	 * First, another object with the same tag can be allocated in place of
51 	 * the buggy object. Also, since the number of entries is limited, the
52 	 * entries relevant to the buggy object can be overwritten.
53 	 */
54 
55 	for (u64 i = pos - 1; i != pos - 1 - stack_ring.size; i--) {
56 		if (alloc_found && free_found)
57 			break;
58 
59 		entry = &stack_ring.entries[i % stack_ring.size];
60 
61 		if (kasan_reset_tag(entry->ptr) != info->object ||
62 		    get_tag(entry->ptr) != get_tag(info->access_addr) ||
63 		    info->cache->object_size != entry->size)
64 			continue;
65 
66 		if (entry->is_free) {
67 			/*
68 			 * Second free of the same object.
69 			 * Give up on trying to find the alloc entry.
70 			 */
71 			if (free_found)
72 				break;
73 
74 			memcpy(&info->free_track, &entry->track,
75 			       sizeof(info->free_track));
76 			free_found = true;
77 
78 			/*
79 			 * If a free entry is found first, the bug is likely
80 			 * a use-after-free.
81 			 */
82 			if (!info->bug_type)
83 				info->bug_type = "slab-use-after-free";
84 		} else {
85 			/* Second alloc of the same object. Give up. */
86 			if (alloc_found)
87 				break;
88 
89 			memcpy(&info->alloc_track, &entry->track,
90 			       sizeof(info->alloc_track));
91 			alloc_found = true;
92 
93 			/*
94 			 * If an alloc entry is found first, the bug is likely
95 			 * an out-of-bounds.
96 			 */
97 			if (!info->bug_type)
98 				info->bug_type = "slab-out-of-bounds";
99 		}
100 	}
101 
102 	write_unlock_irqrestore(&stack_ring.lock, flags);
103 
104 	/* Assign the common bug type if no entries were found. */
105 	if (!info->bug_type)
106 		info->bug_type = get_common_bug_type(info);
107 }
108