1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd
4 *          Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>,
5 *		       Yumiko Sugita <yumiko.sugita.yf@hitachi.com>,
6 *		       Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp>
7 * Copyright (c) 2016 Linux Test Project
8 */
9/*
10 *   test status of errors on man page
11 *   EINTR        v (function was interrupted by a signal)
12 *   EINVAL       v (invalid tv_nsec, etc.)
13 *   ENOTSUP      v (sleep not supported against the specified clock_id)
14 *   EFAULT       v (Invalid request pointer)
15 *   EFAULT       V (Invalid remain pointer when interrupted by a signal)
16 */
17
18#include <limits.h>
19
20#include "time64_variants.h"
21#include "tst_safe_clocks.h"
22#include "tst_sig_proc.h"
23#include "tst_timer.h"
24
25static void sighandler(int sig LTP_ATTRIBUTE_UNUSED)
26{
27}
28
29enum test_type {
30	NORMAL = 1,
31	SEND_SIGINT = 2,
32	BAD_TS_ADDR_REQ = 4,
33	BAD_TS_ADDR_REM = 8,
34};
35
36#define TYPE_NAME(x) .ttype = x, .desc = #x
37
38static void *bad_addr;
39
40struct test_case {
41	clockid_t clk_id;	   /* clock_* clock type parameter */
42	int ttype;		   /* test type (enum) */
43	const char *desc;	   /* test description (name) */
44	int flags;		   /* clock_nanosleep flags parameter */
45	long tv_sec;
46	long tv_nsec;
47	int exp_ret;
48	int exp_err;
49};
50
51static struct test_case tcase[] = {
52	{
53		TYPE_NAME(NORMAL),
54		.clk_id = CLOCK_REALTIME,
55		.flags = 0,
56		.tv_sec = 0,
57		.tv_nsec = -1,
58		.exp_ret = -1,
59		.exp_err = EINVAL,
60	},
61	{
62		TYPE_NAME(NORMAL),
63		.clk_id = CLOCK_REALTIME,
64		.flags = 0,
65		.tv_sec = 0,
66		.tv_nsec = 1000000000,
67		.exp_ret = -1,
68		.exp_err = EINVAL,
69	},
70	{
71		TYPE_NAME(NORMAL),
72		.clk_id = CLOCK_THREAD_CPUTIME_ID,
73		.flags = 0,
74		.tv_sec = 0,
75		.tv_nsec = 500000000,
76		.exp_ret = -1,
77		.exp_err = ENOTSUP,
78	},
79	{
80		TYPE_NAME(SEND_SIGINT),
81		.clk_id = CLOCK_REALTIME,
82		.flags = 0,
83		.tv_sec = 10,
84		.tv_nsec = 0,
85		.exp_ret = -1,
86		.exp_err = EINTR,
87	},
88	{
89		TYPE_NAME(BAD_TS_ADDR_REQ),
90		.clk_id = CLOCK_REALTIME,
91		.flags = 0,
92		.exp_ret = -1,
93		.exp_err = EFAULT,
94	},
95	{
96		TYPE_NAME(BAD_TS_ADDR_REM),
97		.clk_id = CLOCK_REALTIME,
98		.flags = 0,
99		.tv_sec = 10,
100		.tv_nsec = 0,
101		.exp_ret = -1,
102		.exp_err = EFAULT,
103	},
104};
105
106static struct tst_ts *rq;
107static struct tst_ts *rm;
108
109static struct time64_variants variants[] = {
110	{ .clock_nanosleep = libc_clock_nanosleep, .ts_type = TST_LIBC_TIMESPEC, .desc = "vDSO or syscall with libc spec"},
111
112#if (__NR_clock_nanosleep != __LTP__NR_INVALID_SYSCALL)
113	{ .clock_nanosleep = sys_clock_nanosleep, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
114#endif
115
116#if (__NR_clock_nanosleep_time64 != __LTP__NR_INVALID_SYSCALL)
117	{ .clock_nanosleep = sys_clock_nanosleep64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
118#endif
119};
120
121void setup(void)
122{
123	rq->type = variants[tst_variant].ts_type;
124	tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
125	SAFE_SIGNAL(SIGINT, sighandler);
126	bad_addr = tst_get_bad_addr(NULL);
127}
128
129static void do_test(unsigned int i)
130{
131	struct time64_variants *tv = &variants[tst_variant];
132	struct test_case *tc = &tcase[i];
133	pid_t pid = 0;
134	void *request, *remain;
135
136	memset(rm, 0, sizeof(*rm));
137	rm->type = rq->type;
138
139	tst_res(TINFO, "case %s", tc->desc);
140
141	if (tc->ttype & (BAD_TS_ADDR_REQ | BAD_TS_ADDR_REM) &&
142	    tv->clock_nanosleep == libc_clock_nanosleep) {
143		tst_res(TCONF,
144			"The libc wrapper may dereference req or rem");
145		return;
146	}
147
148	if (tc->ttype & (SEND_SIGINT | BAD_TS_ADDR_REM))
149		pid = create_sig_proc(SIGINT, 40, 500000);
150
151	tst_ts_set_sec(rq, tc->tv_sec);
152	tst_ts_set_nsec(rq, tc->tv_nsec);
153
154	if (tc->ttype == BAD_TS_ADDR_REQ)
155		request = bad_addr;
156	else
157		request = tst_ts_get(rq);
158
159	if (tc->ttype == BAD_TS_ADDR_REM)
160		remain = bad_addr;
161	else
162		remain = tst_ts_get(rm);
163
164	TEST(tv->clock_nanosleep(tc->clk_id, tc->flags, request, remain));
165
166	if (tv->clock_nanosleep == libc_clock_nanosleep) {
167		/*
168		 * The return value and error number are differently set for
169		 * libc syscall as compared to kernel syscall.
170		 */
171		if (TST_RET) {
172			TST_ERR = TST_RET;
173			TST_RET = -1;
174		}
175
176		/*
177		 * nsleep isn't implemented by kernelf or
178		 * CLOCK_THREAD_CPUTIME_ID and it returns ENOTSUP, but libc
179		 * changes that error value to EINVAL.
180		 */
181		if (tc->clk_id == CLOCK_THREAD_CPUTIME_ID)
182			tc->exp_err = EINVAL;
183	}
184
185	if (pid) {
186		SAFE_KILL(pid, SIGTERM);
187		SAFE_WAIT(NULL);
188	}
189
190	if (tc->ttype == SEND_SIGINT) {
191		long long expect_ms = tst_ts_to_ms(*rq);
192		long long remain_ms = tst_ts_to_ms(*rm);
193
194		if (tst_ts_valid(rm)) {
195			tst_res(TFAIL | TTERRNO,
196				"The clock_nanosleep() haven't updated"
197				" timespec or it's not valid");
198			return;
199		}
200
201		if (remain_ms > expect_ms) {
202			tst_res(TFAIL | TTERRNO,
203				"remaining time > requested time (%lld > %lld)",
204				remain_ms, expect_ms);
205			return;
206		}
207
208		tst_res(TPASS, "Timespec updated correctly");
209	}
210
211	if ((TST_RET != tc->exp_ret) || (TST_ERR != tc->exp_err)) {
212		tst_res(TFAIL | TTERRNO, "returned %ld, expected %d,"
213			" expected errno: %s (%d)", TST_RET,
214			tc->exp_ret, tst_strerrno(tc->exp_err), tc->exp_err);
215		return;
216	}
217
218	tst_res(TPASS | TTERRNO, "clock_nanosleep() failed with");
219}
220
221static struct tst_test test = {
222	.tcnt = ARRAY_SIZE(tcase),
223	.test = do_test,
224	.test_variants = ARRAY_SIZE(variants),
225	.setup = setup,
226	.forks_child = 1,
227	.bufs = (struct tst_buffers []) {
228		{&rq, .size = sizeof(*rq)},
229		{&rm, .size = sizeof(*rm)},
230		{},
231	}
232};
233