1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci
3f08c3bdfSopenharmony_ci/* Copyright (c) 2019 Michael Moese <mmoese@suse.com>
4f08c3bdfSopenharmony_ci * Regression test for CVE-2017-1000380 based on the original PoC exploit
5f08c3bdfSopenharmony_ci * by Alexander Potapenko <glider@google.com>
6f08c3bdfSopenharmony_ci *
7f08c3bdfSopenharmony_ci * Be careful! This test may crash your kernel!
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * The test performs several ioctl() parallel with readv() on the same
10f08c3bdfSopenharmony_ci * file descriptor to /dev/snd/timer. A buggy kernel will leak memory
11f08c3bdfSopenharmony_ci * to the process, which may contain information from the kernel or
12f08c3bdfSopenharmony_ci * any other process on the system.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci * The issue was fixed with
15f08c3bdfSopenharmony_ci *   http://git.kernel.org/linus/d11662f4f798b50d8c8743f433842c3e40fe3378
16f08c3bdfSopenharmony_ci *   http://git.kernel.org/linus/ba3021b2c79b2fa9114f92790a99deb27a65b728
17f08c3bdfSopenharmony_ci */
18f08c3bdfSopenharmony_ci
19f08c3bdfSopenharmony_ci#include "config.h"
20f08c3bdfSopenharmony_ci#include "tst_test.h"
21f08c3bdfSopenharmony_ci#include "tst_fuzzy_sync.h"
22f08c3bdfSopenharmony_ci#include "tst_safe_macros.h"
23f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h"
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_ci#include <errno.h>
26f08c3bdfSopenharmony_ci#include <fcntl.h>
27f08c3bdfSopenharmony_ci#include <pthread.h>
28f08c3bdfSopenharmony_ci#include <stdio.h>
29f08c3bdfSopenharmony_ci#include <string.h>
30f08c3bdfSopenharmony_ci#include <sys/uio.h>
31f08c3bdfSopenharmony_ci#include <sys/ioctl.h>
32f08c3bdfSopenharmony_ci#include <sound/asound.h>
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci#define MAX_BUFSIZE 1024
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_cistatic int snd_fd;
37f08c3bdfSopenharmony_cistatic struct tst_fzsync_pair fzsync_pair;
38f08c3bdfSopenharmony_ci
39f08c3bdfSopenharmony_cistatic void *ioctl_thread(void *unused)
40f08c3bdfSopenharmony_ci{
41f08c3bdfSopenharmony_ci	int tread_arg = 1;
42f08c3bdfSopenharmony_ci	struct snd_timer_select ts;
43f08c3bdfSopenharmony_ci	struct snd_timer_params tp;
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci	memset(&ts, 0, sizeof(ts));
46f08c3bdfSopenharmony_ci	ts.id.dev_class = 1;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci	memset(&tp, 0, sizeof(tp));
49f08c3bdfSopenharmony_ci	tp.ticks = 1;
50f08c3bdfSopenharmony_ci	tp.filter = 0xf;
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci	while (tst_fzsync_run_b(&fzsync_pair)) {
53f08c3bdfSopenharmony_ci		tst_fzsync_start_race_b(&fzsync_pair);
54f08c3bdfSopenharmony_ci		ioctl(snd_fd, SNDRV_TIMER_IOCTL_TREAD, &tread_arg);
55f08c3bdfSopenharmony_ci		ioctl(snd_fd, SNDRV_TIMER_IOCTL_SELECT, &ts);
56f08c3bdfSopenharmony_ci		ioctl(snd_fd, SNDRV_TIMER_IOCTL_PARAMS, &tp);
57f08c3bdfSopenharmony_ci		ioctl(snd_fd, SNDRV_TIMER_IOCTL_START, 0);
58f08c3bdfSopenharmony_ci		tst_fzsync_end_race_b(&fzsync_pair);
59f08c3bdfSopenharmony_ci	}
60f08c3bdfSopenharmony_ci	return unused;
61f08c3bdfSopenharmony_ci}
62f08c3bdfSopenharmony_ci
63f08c3bdfSopenharmony_cistatic void setup(void)
64f08c3bdfSopenharmony_ci{
65f08c3bdfSopenharmony_ci	if(access("/dev/snd/timer", F_OK))
66f08c3bdfSopenharmony_ci		tst_brk(TCONF, "The file '/dev/snd/timer' is not exist");
67f08c3bdfSopenharmony_ci
68f08c3bdfSopenharmony_ci	tst_fzsync_pair_init(&fzsync_pair);
69f08c3bdfSopenharmony_ci	snd_fd = SAFE_OPEN("/dev/snd/timer",
70f08c3bdfSopenharmony_ci			O_RDONLY|O_CREAT|O_NOCTTY|O_SYNC|O_LARGEFILE, 0);
71f08c3bdfSopenharmony_ci}
72f08c3bdfSopenharmony_ci
73f08c3bdfSopenharmony_cistatic void cleanup(void)
74f08c3bdfSopenharmony_ci{
75f08c3bdfSopenharmony_ci	if (snd_fd > 0)
76f08c3bdfSopenharmony_ci		SAFE_CLOSE(snd_fd);
77f08c3bdfSopenharmony_ci}
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_cistatic void run(void)
80f08c3bdfSopenharmony_ci{
81f08c3bdfSopenharmony_ci	size_t len;
82f08c3bdfSopenharmony_ci	int size;
83f08c3bdfSopenharmony_ci	struct iovec iov;
84f08c3bdfSopenharmony_ci	pthread_t th;
85f08c3bdfSopenharmony_ci	char read_buf[MAX_BUFSIZE];
86f08c3bdfSopenharmony_ci	int i, nz;
87f08c3bdfSopenharmony_ci	pthread_attr_t thread_attr;
88f08c3bdfSopenharmony_ci
89f08c3bdfSopenharmony_ci	pthread_attr_init(&thread_attr);
90f08c3bdfSopenharmony_ci	pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
91f08c3bdfSopenharmony_ci	SAFE_PTHREAD_CREATE(&th, &thread_attr, ioctl_thread, NULL);
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_ci	iov.iov_base = read_buf;
94f08c3bdfSopenharmony_ci	iov.iov_len = sizeof(read_buf) - 1;
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	tst_fzsync_pair_reset(&fzsync_pair, NULL);
97f08c3bdfSopenharmony_ci	while (tst_fzsync_run_a(&fzsync_pair)) {
98f08c3bdfSopenharmony_ci		nz = 0;
99f08c3bdfSopenharmony_ci		memset(read_buf, 0, sizeof(read_buf));
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci		tst_fzsync_start_race_a(&fzsync_pair);
102f08c3bdfSopenharmony_ci		size = readv(snd_fd, &iov, 1);
103f08c3bdfSopenharmony_ci		tst_fzsync_end_race_a(&fzsync_pair);
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci		/* check if it could be a valid ioctl result */
106f08c3bdfSopenharmony_ci		if (size == 0)
107f08c3bdfSopenharmony_ci			continue;
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci		/* check if the buffer is non-empty */
110f08c3bdfSopenharmony_ci		for (i = 0; i < size; i++) {
111f08c3bdfSopenharmony_ci			if (read_buf[i]) {
112f08c3bdfSopenharmony_ci				nz = 1;
113f08c3bdfSopenharmony_ci				break;
114f08c3bdfSopenharmony_ci			}
115f08c3bdfSopenharmony_ci		}
116f08c3bdfSopenharmony_ci		if (!nz)
117f08c3bdfSopenharmony_ci			continue;
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci		len = strlen(read_buf);
120f08c3bdfSopenharmony_ci		/* the kernel's struct snd_timer_read is two unsigned integers*/
121f08c3bdfSopenharmony_ci		if (len <= 2 * sizeof(unsigned int))
122f08c3bdfSopenharmony_ci			continue;
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci		tst_res(TFAIL, "kernel seems vulnerable");
125f08c3bdfSopenharmony_ci		return;
126f08c3bdfSopenharmony_ci	}
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	if (tst_taint_check() != 0)
129f08c3bdfSopenharmony_ci		tst_res(TFAIL, "kernel seems vulnerable");
130f08c3bdfSopenharmony_ci	else
131f08c3bdfSopenharmony_ci		tst_res(TPASS, "kernel seems not vulnerable");
132f08c3bdfSopenharmony_ci}
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_cistatic struct tst_test test = {
135f08c3bdfSopenharmony_ci	.test_all = run,
136f08c3bdfSopenharmony_ci	.setup = setup,
137f08c3bdfSopenharmony_ci	.cleanup = cleanup,
138f08c3bdfSopenharmony_ci	.taint_check = TST_TAINT_W | TST_TAINT_D,
139f08c3bdfSopenharmony_ci	.max_runtime = 150,
140f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
141f08c3bdfSopenharmony_ci		{"linux-git", "d11662f4f798"},
142f08c3bdfSopenharmony_ci		{"linux-git", "ba3021b2c79b"},
143f08c3bdfSopenharmony_ci		{"CVE", "2017-1000380"},
144f08c3bdfSopenharmony_ci		{}
145f08c3bdfSopenharmony_ci	}
146f08c3bdfSopenharmony_ci};
147