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
12extern struct kasan_stack_ring stack_ring;
13
14static 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
30void 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

source code of linux/mm/kasan/report_tags.c