1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019 Linaro Limited. All rights reserved.
4 * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
5 */
6
7/*
8 * Basic tests for errors of clock_settime(2) on different clock types.
9 */
10
11#include "config.h"
12#include "time64_variants.h"
13#include "tst_timer.h"
14#include "tst_safe_clocks.h"
15
16#define DELTA_SEC 10
17
18static void *bad_addr;
19
20struct test_case {
21	clockid_t type;
22	int exp_err;
23	int replace;
24	long tv_sec;
25	long tv_nsec;
26};
27
28struct test_case tc[] = {
29	{				/* case 01: REALTIME: timespec NULL   */
30	 .type = CLOCK_REALTIME,
31	 .exp_err = EFAULT,
32	 .replace = 1,
33	 .tv_sec = 0,
34	 .tv_nsec = 0,
35	 },
36	{				/* case 02: REALTIME: tv_sec = -1     */
37	 .type = CLOCK_REALTIME,
38	 .exp_err = EINVAL,
39	 .replace = 1,
40	 .tv_sec = -1,
41	 .tv_nsec = 0,
42	 },
43	{				/* case 03: REALTIME: tv_nsec = -1    */
44	 .type = CLOCK_REALTIME,
45	 .exp_err = EINVAL,
46	 .replace = 1,
47	 .tv_sec = 0,
48	 .tv_nsec = -1,
49	 },
50	{				/* case 04: REALTIME: tv_nsec = 1s+1  */
51	 .type = CLOCK_REALTIME,
52	 .exp_err = EINVAL,
53	 .replace = 1,
54	 .tv_sec = 0,
55	 .tv_nsec = NSEC_PER_SEC + 1,
56	 },
57	{				/* case 05: MONOTONIC		      */
58	 .type = CLOCK_MONOTONIC,
59	 .exp_err = EINVAL,
60	 },
61	{				/* case 06: MAXCLOCK		      */
62	 .type = MAX_CLOCKS,
63	 .exp_err = EINVAL,
64	 },
65	{				/* case 07: MAXCLOCK+1		      */
66	 .type = MAX_CLOCKS + 1,
67	 .exp_err = EINVAL,
68	 },
69	/* Linux specific */
70	{				/* case 08: CLOCK_MONOTONIC_COARSE    */
71	 .type = CLOCK_MONOTONIC_COARSE,
72	 .exp_err = EINVAL,
73	 },
74	{				/* case 09: CLOCK_MONOTONIC_RAW       */
75	 .type = CLOCK_MONOTONIC_RAW,
76	 .exp_err = EINVAL,
77	 },
78	{				/* case 10: CLOCK_BOOTTIME	      */
79	 .type = CLOCK_BOOTTIME,
80	 .exp_err = EINVAL,
81	 },
82	{				/* case 11: CLOCK_PROCESS_CPUTIME_ID  */
83	 .type = CLOCK_PROCESS_CPUTIME_ID,
84	 .exp_err = EINVAL,
85	 },
86	{				/* case 12: CLOCK_THREAD_CPUTIME_ID   */
87	 .type = CLOCK_THREAD_CPUTIME_ID,
88	 .exp_err = EINVAL,
89	 },
90};
91
92static struct tst_ts spec;
93
94static struct time64_variants variants[] = {
95#if (__NR_clock_settime != __LTP__NR_INVALID_SYSCALL)
96	{ .clock_gettime = sys_clock_gettime, .clock_settime = sys_clock_settime, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
97#endif
98
99#if (__NR_clock_settime64 != __LTP__NR_INVALID_SYSCALL)
100	{ .clock_gettime = sys_clock_gettime64, .clock_settime = sys_clock_settime64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
101#endif
102};
103
104static void setup(void)
105{
106	tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
107
108	bad_addr = tst_get_bad_addr(NULL);
109}
110
111static void verify_clock_settime(unsigned int i)
112{
113	struct time64_variants *tv = &variants[tst_variant];
114	void *ts;
115
116	spec.type = tv->ts_type;
117
118	if (tc[i].replace == 0) {
119		TEST(tv->clock_gettime(CLOCK_REALTIME, tst_ts_get(&spec)));
120		if (TST_RET == -1) {
121			tst_res(TFAIL | TTERRNO, "clock_gettime(2) failed for clock %s",
122				tst_clock_name(CLOCK_REALTIME));
123			return;
124		}
125
126		/* add 1 sec to wall clock */
127		spec = tst_ts_add_us(spec, 1000000);
128	} else {
129		/* use given time spec */
130		tst_ts_set_sec(&spec, tc[i].tv_sec);
131		tst_ts_set_nsec(&spec, tc[i].tv_nsec);
132	}
133
134	/* bad pointer case */
135	if (tc[i].exp_err == EFAULT)
136		ts = bad_addr;
137	else
138		ts = tst_ts_get(&spec);
139
140	TEST(tv->clock_settime(tc[i].type, ts));
141
142	if (TST_RET != -1) {
143		tst_res(TFAIL | TTERRNO, "clock_settime(2): clock %s passed unexpectedly, expected %s",
144			tst_clock_name(tc[i].type),
145			tst_strerrno(tc[i].exp_err));
146		return;
147	}
148
149	if (tc[i].exp_err == TST_ERR) {
150		tst_res(TPASS | TTERRNO, "clock_settime(%s): failed as expected",
151			tst_clock_name(tc[i].type));
152		return;
153	}
154
155	tst_res(TFAIL | TTERRNO, "clock_settime(2): clock %s " "expected to fail with %s",
156		tst_clock_name(tc[i].type), tst_strerrno(tc[i].exp_err));
157}
158
159static struct tst_test test = {
160	.test = verify_clock_settime,
161	.test_variants = ARRAY_SIZE(variants),
162	.setup = setup,
163	.tcnt = ARRAY_SIZE(tc),
164	.needs_root = 1,
165	.restore_wallclock = 1,
166};
167