1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2021 SUSE LLC <mdoucha@suse.cz>
4 * Copyright (c) 2022 Linux Test Project
5 *
6 * CVE-2020-25704
7 *
8 * Check for memory leak in PERF_EVENT_IOC_SET_FILTER ioctl command. Fixed in:
9 *
10 * commit 7bdb157cdebbf95a1cd94ed2e01b338714075d00
11 * Author: kiyin(尹亮) <kiyin@tencent.com>
12 * Date: Wed Nov 4 08:23:22 2020 +0300
13 *
14 * perf/core: Fix a memory leak in perf_event_parse_addr_filter()
15 */
16
17 #include "config.h"
18 #include "tst_test.h"
19 #include "tst_timer.h"
20 #include "lapi/syscalls.h"
21
22 #include "perf_event_open.h"
23
24 #define INTEL_PT_PATH "/sys/bus/event_source/devices/intel_pt/type"
25
26 const int iterations = 12000000;
27 static int fd = -1;
28 static int runtime;
29
setup(void)30 static void setup(void)
31 {
32 struct perf_event_attr ev = {
33 .size = sizeof(struct perf_event_attr),
34 .exclude_kernel = 1,
35 .exclude_hv = 1,
36 .exclude_idle = 1
37 };
38
39 /* intel_pt is currently the only event source that supports filters */
40 if (access(INTEL_PT_PATH, F_OK))
41 tst_brk(TCONF, "intel_pt is not available");
42
43 SAFE_FILE_SCANF(INTEL_PT_PATH, "%d", &ev.type);
44 fd = perf_event_open(&ev, getpid(), -1, -1, 0);
45
46 runtime = tst_remaining_runtime();
47 }
48
49 /*
50 * Check how fast we can do the iterations after 5 seconds of runtime.
51 * If the rate is too small to complete for current runtime then
52 * stop the test.
53 */
check_progress(int i)54 static void check_progress(int i)
55 {
56 static float iter_per_ms;
57 long long elapsed_ms;
58
59 if (iter_per_ms)
60 return;
61
62 if (i % 1000 != 0)
63 return;
64
65 tst_timer_stop();
66 elapsed_ms = tst_timer_elapsed_ms();
67 if (elapsed_ms > 5000) {
68 iter_per_ms = (float) i / elapsed_ms;
69 tst_res(TINFO, "rate: %f iters/ms", iter_per_ms);
70 tst_res(TINFO, "needed rate for current test runtime: %f iters/ms",
71 (float) iterations / (runtime * 1000));
72
73 if (iter_per_ms * 1000 * (runtime - 1) < iterations)
74 tst_brk(TCONF, "System too slow to complete test in specified runtime");
75 }
76 }
77
run(void)78 static void run(void)
79 {
80 long diff, diff_total, mem_avail, mem_avail_prev;
81 int i, sample;
82
83 sample = 0;
84 diff_total = 0;
85
86 mem_avail_prev = SAFE_READ_MEMINFO("MemAvailable:");
87 tst_timer_start(CLOCK_MONOTONIC);
88
89 /* leak about 100MB of RAM */
90 for (i = 0; i < iterations; i++) {
91 ioctl(fd, PERF_EVENT_IOC_SET_FILTER, "filter,0/0@abcd");
92 check_progress(i);
93
94 /*
95 * Every 1200000 iterations, calculate the difference in memory
96 * availability. If the difference is greater than 20 * 1024 (20MB),
97 * increment the sample counter and log the event.
98 */
99 if ((i % 1200000) == 0) {
100 mem_avail = SAFE_READ_MEMINFO("MemAvailable:");
101 diff = mem_avail_prev - mem_avail;
102 diff_total += diff;
103
104 if (diff > 20 * 1024) {
105 sample++;
106 tst_res(TINFO, "MemAvailable decreased by %ld kB at iteration %d", diff, i);
107 }
108
109 mem_avail_prev = mem_avail;
110 }
111 }
112
113 if ((sample > 5) || (diff_total > 100 * 1024))
114 tst_res(TFAIL, "Likely kernel memory leak detected, total decrease: %ld kB", diff_total);
115 else
116 tst_res(TPASS, "No memory leak found");
117 }
118
cleanup(void)119 static void cleanup(void)
120 {
121 if (fd >= 0)
122 SAFE_CLOSE(fd);
123 }
124
125 static struct tst_test test = {
126 .test_all = run,
127 .setup = setup,
128 .cleanup = cleanup,
129 .needs_root = 1,
130 .runtime = 300,
131 .tags = (const struct tst_tag[]) {
132 {"linux-git", "7bdb157cdebb"},
133 {"CVE", "2020-25704"},
134 {}
135 }
136 };
137