| /* |
| * Copyright (c) 2014 Oracle and/or its affiliates. All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| * This test verifies sched_setaffinity(2) for all error conditions |
| * to occur correctly. |
| * |
| * sched_setaffinity() returns -1 and sets the error code to: |
| * |
| * 1) EFAULT, if the supplied memory address is invalid |
| * 2) EINVAL, if the mask doesn't contain at least one |
| * permitted cpu |
| * 3) ESRCH, if the process whose id is pid could not |
| * be found |
| * 4) EPERM, if the calling process doesn't have appropriate |
| * privileges |
| */ |
| |
| #define _GNU_SOURCE |
| #include <errno.h> |
| #include <pwd.h> |
| #include <sched.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include "test.h" |
| #include "safe_macros.h" |
| #include "sched_setaffinity.h" |
| #include "linux_syscall_numbers.h" |
| |
| char *TCID = "sched_setaffinity01"; |
| |
| #define PID_MAX_PATH "/proc/sys/kernel/pid_max" |
| |
| static cpu_set_t *mask, *emask; |
| static cpu_set_t *fmask = (void *)-1; |
| static size_t mask_size, emask_size; |
| static pid_t self_pid, privileged_pid, free_pid; |
| static uid_t uid; |
| static const char nobody_uid[] = "nobody"; |
| static struct passwd *ltpuser; |
| static long ncpus; |
| |
| static struct test_case_t { |
| pid_t *pid; |
| size_t *mask_size; |
| cpu_set_t **mask; |
| int exp_errno; |
| } test_cases[] = { |
| {&self_pid, &mask_size, &fmask, EFAULT}, |
| {&self_pid, &emask_size, &emask, EINVAL}, |
| {&free_pid, &mask_size, &mask, ESRCH}, |
| {&privileged_pid, &mask_size, &mask, EPERM}, |
| }; |
| |
| int TST_TOTAL = ARRAY_SIZE(test_cases); |
| |
| static void cleanup(void) |
| { |
| if (mask != NULL) { |
| CPU_FREE(mask); |
| mask = NULL; |
| } |
| |
| if (emask != NULL) { |
| CPU_FREE(emask); |
| emask = NULL; |
| } |
| |
| SAFE_SETEUID(NULL, uid); |
| |
| if (privileged_pid != 0) { |
| kill(privileged_pid, SIGKILL); |
| waitpid(privileged_pid, NULL, 0); |
| privileged_pid = 0; |
| } |
| } |
| |
| static void setup(void) |
| { |
| tst_require_root(NULL); |
| uid = geteuid(); |
| ncpus = tst_ncpus_max(); |
| |
| /* Current mask */ |
| mask = CPU_ALLOC(ncpus); |
| if (mask == NULL) |
| tst_brkm(TBROK | TERRNO, cleanup, "CPU_ALLOC(%ld) failed", |
| ncpus); |
| mask_size = CPU_ALLOC_SIZE(ncpus); |
| if (sched_getaffinity(0, mask_size, mask) < 0) |
| tst_brkm(TBROK | TERRNO, cleanup, "sched_getaffinity() failed"); |
| |
| /* Mask with one more cpu than available on the system */ |
| emask = CPU_ALLOC(ncpus + 1); |
| if (emask == NULL) |
| tst_brkm(TBROK | TERRNO, cleanup, "CPU_ALLOC(%ld) failed", |
| ncpus + 1); |
| emask_size = CPU_ALLOC_SIZE(ncpus + 1); |
| CPU_ZERO_S(emask_size, emask); |
| CPU_SET_S(ncpus, emask_size, emask); |
| |
| privileged_pid = tst_fork(); |
| if (privileged_pid == 0) { |
| pause(); |
| |
| exit(0); |
| } else if (privileged_pid < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); |
| } |
| |
| /* Dropping the root privileges */ |
| ltpuser = getpwnam(nobody_uid); |
| if (ltpuser == NULL) |
| tst_brkm(TBROK | TERRNO, cleanup, |
| "getpwnam failed for user id %s", nobody_uid); |
| |
| SAFE_SETEUID(cleanup, ltpuser->pw_uid); |
| |
| /* this pid is not used by the OS */ |
| free_pid = tst_get_unused_pid(cleanup); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const char *msg; |
| int lc; |
| int i; |
| |
| msg = parse_opts(argc, argv, NULL, NULL); |
| if (msg != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| setup(); |
| |
| for (lc = 0; TEST_LOOPING(lc); lc++) { |
| tst_count = 0; |
| for (i = 0; i < TST_TOTAL; i++) { |
| /* Avoid calling glibc wrapper function, as it may |
| * try to read/validate data in cpu mask. This test |
| * is passing invalid pointer on purpose. */ |
| TEST(ltp_syscall(__NR_sched_setaffinity, |
| *(test_cases[i].pid), |
| *(test_cases[i].mask_size), |
| *(test_cases[i].mask))); |
| |
| if (TEST_RETURN != -1) |
| tst_resm(TFAIL, |
| "sched_setaffinity() unexpectedly succeded"); |
| |
| if (TEST_ERRNO == test_cases[i].exp_errno) { |
| tst_resm(TPASS, "expected failure with '%s'", |
| strerror(test_cases[i].exp_errno)); |
| } else { |
| tst_resm(TFAIL, |
| "call returned '%s', expected - '%s'", |
| strerror(TEST_ERRNO), |
| strerror(test_cases[i].exp_errno)); |
| } |
| } |
| } |
| |
| cleanup(); |
| tst_exit(); |
| } |