Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* * Copyright (c) 2024 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* compile: xcrun -sdk macosx.internal clang -arch arm64e -arch x86_64 -ldarwintest -o tty_revoke tty_revoke.c */ #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include <sys/param.h> #include <sys/mount.h> #include <sys/mman.h> #include <errno.h> #include <assert.h> #include <stdlib.h> #include <darwintest.h> #include <darwintest/utils.h> T_GLOBAL_META( T_META_NAMESPACE("xnu.vfs"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("vfs"), T_META_ASROOT(true), /* Need root privileges for revoke() */ T_META_CHECK_LEAKS(false)); T_DECL(tty_revoke, "Test that reading revoked terminal devices returns 0 (EOF), not EIO") { int controller_fd, terminal_fd; struct stat sb; char c = 0x41; /* Initial value to verify it gets changed */ ssize_t r; char *terminal_name; T_LOG("Testing revoke behavior on terminal devices"); /* Step 1: Create a pseudoterminal pair using posix_openpt */ T_ASSERT_POSIX_SUCCESS((controller_fd = posix_openpt(O_RDWR)), "Creating pseudoterminal controller"); /* Step 2: Grant access and unlock the terminal */ T_ASSERT_POSIX_SUCCESS(grantpt(controller_fd), "Granting access to pseudoterminal"); T_ASSERT_POSIX_SUCCESS(unlockpt(controller_fd), "Unlocking pseudoterminal"); /* Step 3: Get the terminal device name */ T_ASSERT_NOTNULL((terminal_name = ptsname(controller_fd)), "Getting pseudoterminal device name"); T_LOG("Created pseudoterminal: %s", terminal_name); /* Step 4: Open the terminal side */ T_ASSERT_POSIX_SUCCESS((terminal_fd = open(terminal_name, O_RDONLY)), "Opening pseudoterminal device"); /* Step 5: Verify it's a character device */ T_ASSERT_POSIX_SUCCESS(fstat(terminal_fd, &sb), "Getting stat info for pseudoterminal device"); T_ASSERT_EQ((sb.st_mode & S_IFMT), S_IFCHR, "Verifying pseudoterminal device is a character device"); /* Step 6: Revoke the terminal device */ T_ASSERT_POSIX_SUCCESS(revoke(terminal_name), "Revoking pseudoterminal device"); /* Step 7: Attempt to read from the revoked device */ /* According to the fix, terminal devices should return 0 (EOF) after revoke */ r = read(terminal_fd, &c, 1); T_ASSERT_POSIX_SUCCESS((int)r, "Reading from revoked terminal device should succeed"); T_ASSERT_EQ((long)r, 0L, "Reading from revoked terminal device should return 0 (EOF)"); /* Step 8: Verify the character wasn't modified (since we got EOF) */ T_ASSERT_EQ(c, 0x41, "Buffer should be unchanged when read returns 0"); /* Step 9: Clean up */ T_ASSERT_POSIX_SUCCESS(close(terminal_fd), "Closing revoked pseudoterminal device"); T_ASSERT_POSIX_SUCCESS(close(controller_fd), "Closing pseudoterminal controller"); T_LOG("Test completed successfully - revoked terminal device returned EOF as expected"); } T_DECL(tty_revoke_non_terminal, "Test that reading revoked non-terminal character devices returns EIO") { int fd; struct stat sb; char c = 0x41; ssize_t r; T_LOG("Testing revoke behavior on non-terminal character devices"); /* Try to open a non-terminal character device like /dev/null */ T_ASSERT_POSIX_SUCCESS((fd = open("/dev/null", O_RDONLY)), "Opening /dev/null"); /* Verify it's a character device */ T_ASSERT_POSIX_SUCCESS(fstat(fd, &sb), "Getting stat info for /dev/null"); T_ASSERT_EQ((sb.st_mode & S_IFMT), S_IFCHR, "Verifying /dev/null is a character device"); /* Revoke the device */ T_ASSERT_POSIX_SUCCESS(revoke("/dev/null"), "Revoking /dev/null"); /* Attempt to read from the revoked device */ /* Non-terminal character devices should return EIO after revoke */ r = read(fd, &c, 1); T_ASSERT_EQ((long)r, -1L, "Reading from revoked non-terminal device should fail"); T_ASSERT_EQ(errno, EIO, "Reading from revoked non-terminal device should return EIO"); /* Close the file descriptor */ T_ASSERT_POSIX_SUCCESS(close(fd), "Closing revoked /dev/null"); T_LOG("Test completed successfully - revoked non-terminal device returned EIO as expected"); } T_DECL(tty_revoke_regular_file, "Test that revoking regular files returns ENOTSUP") { int fd; struct stat sb; char template[PATH_MAX]; char *testfile; ssize_t r; T_LOG("Testing revoke behavior on regular files (should fail)"); /* Create a temporary file */ snprintf(template, sizeof(template), "%s/tty_revoke_regular_file-XXXXXX", dt_tmpdir()); T_ASSERT_POSIX_SUCCESS((fd = mkstemp(template)), "Creating temporary file"); testfile = template; /* Write some data to it */ T_ASSERT_POSIX_SUCCESS((int)write(fd, "test", 4), "Writing to temporary file"); T_ASSERT_POSIX_SUCCESS(close(fd), "Closing temporary file after write"); /* Reopen for reading */ T_ASSERT_POSIX_SUCCESS((fd = open(testfile, O_RDONLY)), "Reopening temporary file for reading"); /* Verify it's a regular file */ T_ASSERT_POSIX_SUCCESS(fstat(fd, &sb), "Getting stat info for temporary file"); T_ASSERT_EQ((sb.st_mode & S_IFMT), S_IFREG, "Verifying temporary file is a regular file"); /* Attempt to revoke the regular file - this should fail */ r = revoke(testfile); T_ASSERT_EQ((long)r, -1L, "Revoking regular file should fail"); T_ASSERT_EQ(errno, ENOTSUP, "Revoking regular file should return ENOTSUP"); /* Clean up */ T_ASSERT_POSIX_SUCCESS(close(fd), "Closing temporary file"); T_ASSERT_POSIX_SUCCESS(unlink(testfile), "Removing temporary file"); T_LOG("Test completed successfully - revoke correctly failed on regular file"); } |