• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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