/* * Copyright (C) 2012 Linux Test Project, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 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. * * Further, this software is distributed without any warranty that it * is free of the rightful claim of any third person regarding * infringement or the like. Any license provided herein, whether * implied or otherwise, applies only to this software file. Patent * licenses, if any, provided herein do not apply to combinations of * this program with other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /* * errno tests shared by process_vm_readv, process_vm_writev tests. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "test.h" #include "safe_macros.h" #include "process_vm.h" struct process_vm_params { int len; char *ldummy; char *rdummy; pid_t pid; struct iovec *lvec; unsigned long liovcnt; struct iovec *rvec; unsigned long riovcnt; unsigned long flags; }; static int rflag; static int wflag; static option_t options[] = { {"r", &rflag, NULL}, {"w", &wflag, NULL}, {NULL, NULL, NULL} }; static char TCID_readv[] = "process_vm_readv"; static char TCID_writev[] = "process_vm_writev"; char *TCID = "cma01"; int TST_TOTAL = 1; static void (*cma_test_params) (struct process_vm_params * params) = NULL; static void setup(char *argv[]); static void cleanup(void); static void help(void); static void cma_test_params_read(struct process_vm_params *params); static void cma_test_params_write(struct process_vm_params *params); static void cma_test_errnos(void); int main(int argc, char *argv[]) { int lc; tst_parse_opts(argc, argv, options, &help); setup(argv); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; cma_test_errnos(); } cleanup(); tst_exit(); } static void setup(char *argv[]) { tst_require_root(); if (rflag && wflag) tst_brkm(TBROK, NULL, "Parameters -r -w can not be used" " at the same time."); else if (rflag) { TCID = TCID_readv; #if defined(__NR_process_vm_readv) cma_test_params = cma_test_params_read; #else tst_brkm(TCONF, NULL, "process_vm_readv does not" " exist on your system."); #endif } else if (wflag) { TCID = TCID_writev; #if defined(__NR_process_vm_writev) cma_test_params = cma_test_params_write; #else tst_brkm(TCONF, NULL, "process_vm_writev does not" " exist on your system."); #endif } else tst_brkm(TBROK, NULL, "Parameter missing, required -r or -w."); TEST_PAUSE; } static void cleanup(void) { } static void help(void) { printf(" -r Use process_vm_readv\n"); printf(" -w Use process_vm_writev\n"); } static void cma_test_params_read(struct process_vm_params *params) { TEST(test_process_vm_readv(params->pid, params->lvec, params->liovcnt, params->rvec, params->riovcnt, params->flags)); } static void cma_test_params_write(struct process_vm_params *params) { TEST(test_process_vm_writev(params->pid, params->lvec, params->liovcnt, params->rvec, params->riovcnt, params->flags)); } static int cma_check_ret(long expected_ret, long act_ret) { if (expected_ret == act_ret) { tst_resm(TPASS, "expected ret success - " "returned value = %ld", act_ret); } else { tst_resm(TFAIL, "unexpected failure - " "returned value = %ld, expected: %ld", act_ret, expected_ret); return 1; } return 0; } static int cma_check_errno(long expected_errno) { if (TEST_ERRNO == expected_errno) tst_resm(TPASS | TTERRNO, "expected failure"); else if (TEST_ERRNO == 0) { tst_resm(TFAIL, "call succeeded unexpectedly"); return 1; } else { tst_resm(TFAIL | TTERRNO, "unexpected failure - " "expected = %ld : %s, actual", expected_errno, strerror(expected_errno)); return 2; } return 0; } static struct process_vm_params *cma_alloc_sane_params(void) { struct process_vm_params *sane_params; int len; len = getpagesize(); sane_params = SAFE_MALLOC(NULL, sizeof(struct process_vm_params)); sane_params->len = len; sane_params->ldummy = SAFE_MALLOC(NULL, len); sane_params->rdummy = SAFE_MALLOC(NULL, len); sane_params->lvec = SAFE_MALLOC(NULL, sizeof(struct iovec)); sane_params->lvec->iov_base = sane_params->ldummy; sane_params->lvec->iov_len = len; sane_params->liovcnt = 1; sane_params->rvec = SAFE_MALLOC(NULL, sizeof(struct iovec)); sane_params->rvec->iov_base = sane_params->rdummy; sane_params->rvec->iov_len = len; sane_params->riovcnt = 1; sane_params->flags = 0; sane_params->pid = getpid(); return sane_params; } static void cma_free_params(struct process_vm_params *params) { if (params) { free(params->ldummy); free(params->rdummy); free(params->lvec); free(params->rvec); free(params); } } static void cma_test_sane_params(void) { struct process_vm_params *sane_params; sane_params = cma_alloc_sane_params(); tst_resm(TINFO, "test_sane_params"); cma_test_params(sane_params); cma_check_ret(sane_params->len, TEST_RETURN); cma_free_params(sane_params); } static void cma_test_flags(void) { struct process_vm_params *params; long flags[] = { -INT_MAX, -1, 1, INT_MAX, 0 }; int flags_size = sizeof(flags) / sizeof(flags[0]); int i; params = cma_alloc_sane_params(); for (i = 0; i < flags_size; i++) { params->flags = flags[i]; tst_resm(TINFO, "test_flags, flags=%ld", flags[i]); cma_test_params(params); /* atm. only flags == 0 is allowed, everything else * should fail with EINVAL */ if (flags[i] != 0) { cma_check_ret(-1, TEST_RETURN); cma_check_errno(EINVAL); } else { cma_check_ret(params->len, TEST_RETURN); } } cma_free_params(params); } static void cma_test_iov_len_overflow(void) { struct process_vm_params *params; ssize_t maxlen = -1; params = cma_alloc_sane_params(); params->lvec->iov_len = maxlen; params->rvec->iov_len = maxlen; tst_resm(TINFO, "test_iov_len_overflow"); cma_test_params(params); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EINVAL); cma_free_params(params); } static void cma_test_iov_invalid(void) { struct process_vm_params *sane_params; struct process_vm_params params_copy; sane_params = cma_alloc_sane_params(); /* make a shallow copy we can 'damage' */ params_copy = *sane_params; tst_resm(TINFO, "test_iov_invalid - lvec->iov_base"); params_copy.lvec->iov_base = (void *)-1; cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); params_copy = *sane_params; tst_resm(TINFO, "test_iov_invalid - rvec->iov_base"); params_copy.rvec->iov_base = (void *)-1; cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); params_copy = *sane_params; tst_resm(TINFO, "test_iov_invalid - lvec"); params_copy.lvec = (void *)-1; cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); params_copy = *sane_params; tst_resm(TINFO, "test_iov_invalid - rvec"); params_copy.rvec = (void *)-1; cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); cma_free_params(sane_params); } static void cma_test_invalid_pid(void) { pid_t invalid_pid = -1; struct process_vm_params *params; params = cma_alloc_sane_params(); tst_resm(TINFO, "test_invalid_pid"); params->pid = invalid_pid; cma_test_params(params); cma_check_ret(-1, TEST_RETURN); cma_check_errno(ESRCH); cma_free_params(params); invalid_pid = tst_get_unused_pid(cleanup); params = cma_alloc_sane_params(); params->pid = invalid_pid; cma_test_params(params); cma_check_ret(-1, TEST_RETURN); cma_check_errno(ESRCH); cma_free_params(params); } static void cma_test_invalid_perm(void) { char nobody_uid[] = "nobody"; struct passwd *ltpuser; int status; struct process_vm_params *params; pid_t child_pid; pid_t parent_pid; int ret = 0; tst_resm(TINFO, "test_invalid_perm"); parent_pid = getpid(); child_pid = fork(); switch (child_pid) { case -1: tst_brkm(TBROK | TERRNO, cleanup, "fork"); break; case 0: ltpuser = getpwnam(nobody_uid); if (ltpuser == NULL) tst_brkm(TBROK | TERRNO, NULL, "getpwnam failed"); SAFE_SETUID(NULL, ltpuser->pw_uid); params = cma_alloc_sane_params(); params->pid = parent_pid; cma_test_params(params); ret |= cma_check_ret(-1, TEST_RETURN); ret |= cma_check_errno(EPERM); cma_free_params(params); exit(ret); default: SAFE_WAITPID(cleanup, child_pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) tst_resm(TFAIL, "child returns %d", status); } } static void cma_test_invalid_protection(void) { struct process_vm_params *sane_params; struct process_vm_params params_copy; void *p; sane_params = cma_alloc_sane_params(); /* make a shallow copy we can 'damage' */ p = mmap(NULL, getpagesize(), PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (p == MAP_FAILED) tst_brkm(TBROK | TERRNO, cleanup, "mmap"); params_copy = *sane_params; params_copy.lvec->iov_base = p; tst_resm(TINFO, "test_invalid_protection lvec"); cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); params_copy = *sane_params; params_copy.rvec->iov_base = p; tst_resm(TINFO, "test_invalid_protection rvec"); cma_test_params(¶ms_copy); cma_check_ret(-1, TEST_RETURN); cma_check_errno(EFAULT); SAFE_MUNMAP(cleanup, p, getpagesize()); cma_free_params(sane_params); } static void cma_test_errnos(void) { cma_test_sane_params(); cma_test_flags(); cma_test_iov_len_overflow(); cma_test_iov_invalid(); cma_test_invalid_pid(); cma_test_invalid_perm(); cma_test_invalid_protection(); }