/*++ Copyright (c) Microsoft. All rights reserved. Module Name: Ioprio.c Abstract: This file is a ioprio test. --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include "lxtutil.h" #if !defined(__amd64__) && !defined(__aarch64__) #include #else #include #include #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #ifndef O_PATH #define O_PATH 010000000 #endif #endif #define IOPRIO_UID 1007 #define IOPRIO_GID 1007 // // When the ioprio.h header is available, these types can be removed. // #define LX_IOPRIO_WHO_PROCESS 1 #define LX_IOPRIO_WHO_PGRP 2 #define LX_IOPRIO_WHO_USER 3 #define LX_IOPRIO_CLASS_NONE 0 #define LX_IOPRIO_CLASS_RT 1 #define LX_IOPRIO_CLASS_BE 2 #define LX_IOPRIO_CLASS_IDLE 3 #define LX_IOPRIO_CLASS_SHIFT 13 #define LX_IOPRIO_PRIO_MASK ((1 << LX_IOPRIO_CLASS_SHIFT) - 1) #define LX_IOPRIO_PRIO_CLASS(_mask) ((_mask) >> LX_IOPRIO_CLASS_SHIFT) #define LX_IOPRIO_PRIO_DATA(_mask) ((_mask) & LX_IOPRIO_PRIO_MASK) #define LX_IOPRIO_PRIO_VALUE(_class, _data) (((_class) << LX_IOPRIO_CLASS_SHIFT) | (_data)) #define LX_IO_DEFAULT_PRIORITY 4 #define LXT_NAME "Ioprio" int IoprioVariationGetProcess(PLXT_ARGS Args); int IoprioVariationSetProcess(PLXT_ARGS Args); int IoprioVariationGetSetPriority(PLXT_ARGS Args); // // Global constants // static const LXT_VARIATION g_LxtVariations[] = { {"ioprio_get process", IoprioVariationGetProcess}, {"getpriority / setpriority", IoprioVariationGetSetPriority}, {"ioprio_set process", IoprioVariationSetProcess}}; int IoprioTestEntry(int Argc, char* Argv[]) /*++ --*/ { LXT_ARGS Args; int Result; LxtInitialize(Argc, Argv, &Args, LXT_NAME); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return !LXT_SUCCESS(Result); } int IoprioVariationGetProcess(PLXT_ARGS Args) /*++ --*/ { int DefaultPriority; int IoPrio; int Result; // // Negative variations. // LxtCheckErrnoFailure(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, -1), ESRCH); LxtCheckErrnoFailure(IoPrio = LxtIoprio_get(0, 0), EINVAL); // // Get the default priority for the current and parent thread. // LxtCheckErrno(DefaultPriority = getpriority(PRIO_PROCESS, 0)); LxtLogInfo("DefaultPriority: %d", DefaultPriority); LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, "%d"); LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, getpid())); LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, "%d"); LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, getppid())); LxtCheckEqual(IoPrio, LX_IO_DEFAULT_PRIORITY, "%d"); // // Set the priority of the current thread and check that it does not change // the io priority. // LxtCheckErrno(setpriority(PRIO_PROCESS, 0, LX_IO_DEFAULT_PRIORITY + 1)); LxtCheckErrno(IoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckNotEqual(IoPrio, LX_IO_DEFAULT_PRIORITY + 1, "%d"); Result = LXT_RESULT_SUCCESS; ErrorExit: setpriority(PRIO_PROCESS, 0, LX_IO_DEFAULT_PRIORITY); return Result; } int IoprioVariationSetProcess(PLXT_ARGS Args) /*++ --*/ { struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int DefaultPriority; int Index; int InitialIoPrio; int IoPrio; int NewIoPrio; int Result; ChildPid = -1; // // Get the default priority for the current and parent thread. // // N.B. Setting the IO priority to this default value fails. // LxtCheckErrno(InitialIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtLogInfo("InitialIoPrio = %d", InitialIoPrio); IoPrio = InitialIoPrio; LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // LX_IOPRIO_CLASS_NONE // for (Index = 0; Index < 1; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // LX_IOPRIO_CLASS_RT // for (Index = 0; Index < 8; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // LX_IOPRIO_CLASS_BE // for (Index = 0; Index < 8; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // LX_IOPRIO_CLASS_IDLE // for (Index = 0; Index < 0x8000; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, getppid(), IoPrio)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; // // Drop all privileges but CAP_SYS_ADMIN. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(IOPRIO_UID)); LxtCheckErrno(setuid(IOPRIO_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); // // Attempt to change parent's io priority now that UID / GID do not match. // IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, getppid(), IoPrio), EPERM); for (Index = 0; Index < 8; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // Drop all privileges. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EPERM); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_RT, 8); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EPERM); // // LX_IOPRIO_CLASS_NONE // IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 1); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); // // LX_IOPRIO_CLASS_BE // for (Index = 0; Index < 8; Index += 1) { IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index); LxtCheckErrno(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio)); LxtCheckErrno(NewIoPrio = LxtIoprio_get(LX_IOPRIO_WHO_PROCESS, 0)); LxtCheckEqual(IoPrio, NewIoPrio, "%d"); } IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_BE, Index); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); goto ErrorExit; } LxtCheckResult(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Negative variations. // IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, -1, IoPrio), ESRCH); LxtCheckErrnoFailure(LxtIoprio_set(0, 0, IoPrio), EINVAL); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_IDLE + 1, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); IoPrio = LX_IOPRIO_PRIO_VALUE(-1, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_PROCESS, 0, IoPrio), EINVAL); LxtCheckErrnoFailure(LxtIoprio_set(0, 0, IoPrio), EINVAL); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 0); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, 0, IoPrio), EINVAL); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, -1, IoPrio), EINVAL); IoPrio = LX_IOPRIO_PRIO_VALUE(LX_IOPRIO_CLASS_NONE, 1); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, 0, IoPrio), EINVAL); LxtCheckErrnoFailure(LxtIoprio_set(LX_IOPRIO_WHO_USER + 1, -1, IoPrio), EINVAL); Result = LXT_RESULT_SUCCESS; ErrorExit: if (ChildPid == 0) { _exit(Result); } return Result; } int IoprioSetPriority(int Which, int Who, int Priority, int ExpectedPriority) { int Result; LxtCheckErrno(setpriority(Which, Who, Priority)); errno = 0; Result = getpriority(Which, Who); if ((Result != ExpectedPriority) || (errno != 0)) { LxtLogError("getpriority returned %d expected %d - errno %d", Result, ExpectedPriority, errno); Result = LXT_RESULT_FAILURE; goto ErrorExit; } Result = LXT_RESULT_SUCCESS; ErrorExit: return Result; } int IoprioVariationGetSetPriority(PLXT_ARGS Args) { int ChildPid; int OriginalPriority; pid_t ParentPid; int Result; struct rlimit Rlimit; ChildPid = -1; errno = 0; OriginalPriority = getpriority(PRIO_PROCESS, 0); LxtLogInfo("OriginalPriority %d", OriginalPriority); // // Validate setting the priority to a negative number. // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -1, -1)); // // Validate setting the priority to a positive number. // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5)); // // Validate the lower bound of the priority (-20). // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -20, -20)); LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -21, -20)); LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, -444, -20)); // // Validate the upper bound of the priority (19). // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 19, 19)); LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 20, 19)); LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 444, 19)); // // Invalid parameter variations. // LxtCheckErrnoFailure(getpriority(-1, 0), EINVAL); // // Permissions checks. Reset the priority to a known value. // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5)); ParentPid = getpid(); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Change the UID to drop CAP_SYS_NICE and verify that the priority can // be set to the same value. // LxtCheckErrno(setuid(1000)); LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 5, 5)); // // Attempt to lower the priority. // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 6, 6)); // // Attempt to raise the priority (should fail). // LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, 0, 4), EACCES); // // Attempt to modify the parent's priority (should fail). // LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, ParentPid, 5), EPERM); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Change the nice rlimit value. // Rlimit.rlim_cur = 19; Rlimit.rlim_max = 19; LxtCheckErrno(setrlimit(RLIMIT_NICE, &Rlimit)); // // Change the UID to drop CAP_SYS_NICE. // LxtCheckErrno(setuid(1000)); // // Setting the priority to 1 should succeed. // LxtCheckResult(IoprioSetPriority(PRIO_PROCESS, 0, 1, 1)); // // Setting the priority to 0 should fail. // LxtCheckErrnoFailure(setpriority(PRIO_PROCESS, 0, 0), EACCES); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (ChildPid == 0) { _exit(Result); } if (OriginalPriority > 0) { setpriority(PRIO_PROCESS, 0, OriginalPriority); } return Result; }