170 lines
3.8 KiB
C
170 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
* Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved.
|
|
* AUTHOR : Saji Kumar.V.R <saji.kumar@wipro.com>
|
|
*/
|
|
|
|
/*\
|
|
* [Description]
|
|
*
|
|
* Tests for adjtimex() error conditions:
|
|
*
|
|
* - EPERM with SET_MODE as nobody
|
|
* - EFAULT with SET_MODE and invalid timex pointer
|
|
* - EINVAL with ADJ_TICK greater than max tick
|
|
* - EINVAL with ADJ_TICK smaller than min tick
|
|
*
|
|
* On kernels older than 2.6.26:
|
|
*
|
|
* - EINVAL with AJD_OFFSET smaller than min offset
|
|
* - EINVAL with AJD_OFFSET greater than max offset
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <sys/timex.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include "tst_test.h"
|
|
#include "lapi/syscalls.h"
|
|
|
|
#define SET_MODE (ADJ_OFFSET | ADJ_FREQUENCY | ADJ_MAXERROR | ADJ_ESTERROR | \
|
|
ADJ_STATUS | ADJ_TIMECONST | ADJ_TICK)
|
|
|
|
static int hz; /* HZ from sysconf */
|
|
|
|
static struct timex *tim_save, *buf;
|
|
static struct passwd *ltpuser;
|
|
|
|
static int libc_adjtimex(struct timex *value)
|
|
{
|
|
return adjtimex(value);
|
|
}
|
|
|
|
static int sys_adjtimex(struct timex *value)
|
|
{
|
|
return tst_syscall(__NR_adjtimex, value);
|
|
}
|
|
|
|
static struct test_case {
|
|
unsigned int modes;
|
|
long lowlimit;
|
|
long highlimit;
|
|
long delta;
|
|
int exp_err;
|
|
} tc[] = {
|
|
{.modes = SET_MODE, .exp_err = EPERM},
|
|
{.modes = SET_MODE, .exp_err = EFAULT},
|
|
{.modes = ADJ_TICK, .lowlimit = 900000, .delta = 1, .exp_err = EINVAL},
|
|
{.modes = ADJ_TICK, .highlimit = 1100000, .delta = 1, .exp_err = EINVAL},
|
|
{.modes = ADJ_OFFSET, .highlimit = 512000L, .delta = 1, .exp_err = EINVAL},
|
|
{.modes = ADJ_OFFSET, .lowlimit = -512000L, .delta = -1, .exp_err = EINVAL},
|
|
};
|
|
|
|
static struct test_variants
|
|
{
|
|
int (*adjtimex)(struct timex *value);
|
|
char *desc;
|
|
} variants[] = {
|
|
{ .adjtimex = libc_adjtimex, .desc = "libc adjtimex()"},
|
|
|
|
#if (__NR_adjtimex != __LTP__NR_INVALID_SYSCALL)
|
|
{ .adjtimex = sys_adjtimex, .desc = "__NR_adjtimex syscall"},
|
|
#endif
|
|
};
|
|
|
|
static void verify_adjtimex(unsigned int i)
|
|
{
|
|
struct timex *bufp;
|
|
struct test_variants *tv = &variants[tst_variant];
|
|
|
|
*buf = *tim_save;
|
|
buf->modes = tc[i].modes;
|
|
bufp = buf;
|
|
|
|
if (tc[i].exp_err == EPERM)
|
|
SAFE_SETEUID(ltpuser->pw_uid);
|
|
|
|
if (tc[i].exp_err == EINVAL) {
|
|
if (tc[i].modes == ADJ_TICK) {
|
|
if (tc[i].lowlimit)
|
|
buf->tick = tc[i].lowlimit - tc[i].delta;
|
|
|
|
if (tc[i].highlimit)
|
|
buf->tick = tc[i].highlimit + tc[i].delta;
|
|
}
|
|
if (tc[i].modes == ADJ_OFFSET) {
|
|
if (tc[i].lowlimit || tc[i].highlimit) {
|
|
tst_res(TCONF, "Newer kernels normalize offset value outside range");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tc[i].exp_err == EFAULT) {
|
|
if (tv->adjtimex != libc_adjtimex) {
|
|
bufp = (struct timex *) -1;
|
|
} else {
|
|
tst_res(TCONF, "EFAULT is skipped for libc variant");
|
|
return;
|
|
}
|
|
}
|
|
|
|
TST_EXP_FAIL2(tv->adjtimex(bufp), tc[i].exp_err, "adjtimex() error");
|
|
|
|
if (tc[i].exp_err == EPERM)
|
|
SAFE_SETEUID(0);
|
|
}
|
|
|
|
static void setup(void)
|
|
{
|
|
struct test_variants *tv = &variants[tst_variant];
|
|
size_t i;
|
|
|
|
tst_res(TINFO, "Testing variant: %s", tv->desc);
|
|
|
|
tim_save->modes = 0;
|
|
|
|
ltpuser = SAFE_GETPWNAM("nobody");
|
|
SAFE_SETEUID(ltpuser->pw_uid);
|
|
|
|
/* set the HZ from sysconf */
|
|
hz = SAFE_SYSCONF(_SC_CLK_TCK);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tc); i++) {
|
|
if (tc[i].modes == ADJ_TICK) {
|
|
tc[i].highlimit /= hz;
|
|
tc[i].lowlimit /= hz;
|
|
}
|
|
}
|
|
|
|
if ((adjtimex(tim_save)) == -1) {
|
|
tst_brk(TBROK | TERRNO,
|
|
"adjtimex(): failed to save current params");
|
|
}
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
|
|
tim_save->modes = SET_MODE;
|
|
|
|
/* Restore saved parameters */
|
|
if ((adjtimex(tim_save)) == -1)
|
|
tst_res(TWARN, "Failed to restore saved parameters");
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.test = verify_adjtimex,
|
|
.setup = setup,
|
|
.cleanup = cleanup,
|
|
.tcnt = ARRAY_SIZE(tc),
|
|
.test_variants = ARRAY_SIZE(variants),
|
|
.needs_root = 1,
|
|
.bufs = (struct tst_buffers []) {
|
|
{&buf, .size = sizeof(*buf)},
|
|
{&tim_save, .size = sizeof(*tim_save)},
|
|
{},
|
|
}
|
|
};
|