1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2016 Linux Test Project.
4 */
5
6/*
7 * A regression test for can_nice call usage in sched_setscheduler,
8 * introduced by kernel commit:
9 *    d50dde5a (sched: Add new scheduler syscalls to support
10 *
11 * This was fixed by below commit:
12 *    eaad4513 (sched: Fix __sched_setscheduler() nice test
13 *
14 */
15
16#define _GNU_SOURCE
17#include <stdio.h>
18#include <errno.h>
19#include <pwd.h>
20#include <sys/time.h>
21#include <sys/resource.h>
22#include <sys/wait.h>
23#include <stdlib.h>
24
25#include "tst_test.h"
26#include "tst_sched.h"
27
28#define RLIMIT_NICE_NORMAL 20
29
30static pid_t zero_pid;
31static struct sched_param param[1] = { {0} };
32
33struct test_case_t {
34	pid_t *pid;
35	int policy;
36	struct sched_param *sched_param;
37	int error;
38};
39
40struct test_case_t cases[] = {
41	{
42		.pid = &zero_pid,
43		.policy = SCHED_OTHER,
44		.sched_param = &param[0]
45	},
46	{
47		.pid = &zero_pid,
48		.policy = SCHED_BATCH,
49		.sched_param = &param[0]
50	},
51#ifdef SCHED_IDLE
52	{
53		.pid = &zero_pid,
54		.policy = SCHED_IDLE,
55		.sched_param = &param[0]
56	}
57#endif
58};
59
60static void l_rlimit_show(const int type, struct rlimit *limit)
61{
62	SAFE_GETRLIMIT(type, limit);
63	tst_res(TINFO,
64		"rlimit rlim_cur=%lu", (unsigned long)(limit->rlim_cur));
65	tst_res(TINFO,
66		"rlimit rlim_max=%lu", (unsigned long)(limit->rlim_max));
67}
68
69static void l_rlimit_setup(const int type, struct rlimit *limit)
70{
71	struct rlimit tmp_rlimit;
72
73	tst_res(TINFO,
74		"Setting rlim_cur to %lu", (unsigned long)(limit->rlim_cur));
75	tst_res(TINFO,
76		"Setting rlim_max to %lu", (unsigned long)(limit->rlim_max));
77
78	SAFE_SETRLIMIT(type, limit);
79
80	l_rlimit_show(RLIMIT_NICE, &tmp_rlimit);
81
82	if (tmp_rlimit.rlim_cur != limit->rlim_cur)
83		tst_brk(TBROK | TERRNO, "Expect rlim_cur = %lu, get %lu",
84				(unsigned long)(limit->rlim_cur),
85				(unsigned long)tmp_rlimit.rlim_cur);
86
87	if (tmp_rlimit.rlim_max != limit->rlim_max)
88		tst_brk(TBROK | TERRNO, "Expect rlim_max = %lu, get %lu",
89				(unsigned long)(limit->rlim_max),
90				(unsigned long)(tmp_rlimit.rlim_max));
91}
92
93static void verify_fn(unsigned int i)
94{
95	struct sched_variant *tv = &sched_variants[tst_variant];
96
97	tst_res(TINFO, "Verifying case[%d]: policy = %d, priority = %d",
98		i + 1, cases[i].policy, cases[i].sched_param->sched_priority);
99
100	TST_EXP_PASS(tv->sched_setscheduler(*cases[i].pid, cases[i].policy,
101		     cases[i].sched_param));
102}
103
104static void setup(void)
105{
106	struct sched_variant *tv = &sched_variants[tst_variant];
107	uid_t ruid, euid, suid;
108	struct rlimit limit;
109	struct passwd *pw;
110	uid_t nobody_uid;
111
112	tst_res(TINFO, "Testing %s variant", tv->desc);
113
114	pw = SAFE_GETPWNAM("nobody");
115	nobody_uid = pw->pw_uid;
116	l_rlimit_show(RLIMIT_NICE, &limit);
117
118	/*
119	* nice rlimit ranges from 1 to 40, mapping to real nice
120	* value from 19 to -20. We set it to 19, as the default priority
121	* of process with fair policy is 120, which will be translated
122	* into nice 20, we make this RLIMIT_NICE smaller than that, to
123	* verify the can_nice usage issue.
124	*/
125	limit.rlim_cur = (RLIMIT_NICE_NORMAL - 1);
126	limit.rlim_max = (RLIMIT_NICE_NORMAL - 1);
127
128	l_rlimit_setup(RLIMIT_NICE, &limit);
129
130	tst_res(TINFO, "Setting init sched policy to SCHED_OTHER");
131	if (tv->sched_setscheduler(0, SCHED_OTHER, &param[0]))
132		tst_brk(TBROK | TERRNO, "sched_setscheduler(0, SCHED_OTHER, 0)");
133	if (tv->sched_getscheduler(0) != SCHED_OTHER)
134		tst_brk(TBROK | TERRNO, "sched_getscheduler(0) != SCHED_OTHER");
135
136	tst_res(TINFO, "Setting euid to nobody to drop privilege");
137
138	SAFE_SETEUID(nobody_uid);
139	SAFE_GETRESUID(&ruid, &euid, &suid);
140	if (euid != nobody_uid)
141		tst_brk(TBROK | TERRNO, "ERROR seteuid(nobody_uid)");
142}
143
144static void do_test(unsigned int i)
145{
146	int status = 0;
147	pid_t f_pid = SAFE_FORK();
148
149	if (f_pid == 0) {
150		tst_res(TINFO, "forked pid is %d", getpid());
151		verify_fn(i);
152		exit(0);
153	}
154
155	SAFE_WAIT(&status);
156}
157
158static struct tst_test test = {
159	.test_variants = ARRAY_SIZE(sched_variants),
160	.tcnt = ARRAY_SIZE(cases),
161	.test = do_test,
162	.setup = setup,
163	.needs_root = 1,
164	.forks_child = 1
165};
166
167