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