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 | |
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 | |
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(v: &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(addr: 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 | |