/*++ Copyright (c) Microsoft. All rights reserved. Module Name: shm.c Abstract: This file is a test for the system V shared memory family of system calls. --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__amd64__) && !defined(__aarch64__) #include #else #include #include #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #ifndef O_PATH #define O_PATH 010000000 #endif #endif #include "lxtcommon.h" #include "unittests.h" #define LXT_NAME "shm" #define SHM_ACCESS_UID 1004 #define SHM_ACCESS_GID 1004 // // Globals. // bool g_RunningOnNative = false; bool g_VerboseShm = false; int ShmAtAccess(PLXT_ARGS Args); int ShmAtDtSyscall(PLXT_ARGS Args); int ShmCtlSyscall(PLXT_ARGS Args); int ShmGetAccess(PLXT_ARGS Args); int ShmGetSyscall(PLXT_ARGS Args); int ShmPidNamespace(PLXT_ARGS Args); void ShmPrintInfo(struct shmid_ds* Stat); void ShmPrintInfoAttach(struct shmid_ds* Stat); static const LXT_VARIATION g_LxtVariations[] = { {"shmget syscall", ShmGetSyscall}, {"shmget access", ShmGetAccess}, {"shmctl syscall", ShmCtlSyscall}, {"shmat / shmdt syscalls", ShmAtDtSyscall}, {"shmat access", ShmAtAccess}, {"shm pid namespace", ShmPidNamespace}}; int ShmTestEntry(int Argc, char* Argv[]) { LXT_ARGS Args; int Result; LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); LXT_SYNCHRONIZATION_POINT_INIT(); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return 0; } int ShmAtAccess(PLXT_ARGS Args) { unsigned char* Address; struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int Id; void* MapResult; int Result; Address = NULL; ChildPid = -1; Id = -1; // // Create a shared memory region that should be unmappable by a process // without the CAP_IPC_OWNER capability. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(setuid(SHM_ACCESS_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Drop the CAP_IPC_OWNER capability and attempt to map again (should fail). // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a read only memory region and verify that it is only mappable as // read only by the owner. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0400)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Drop the CAP_IPC_OWNER capability and attempt to with the readonly // flag. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Attempt to map as read / write (should fail). // Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Attempt to map as execute (should fail). // Address = LxtShmAt(Id, NULL, SHM_EXEC); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } Id = -1; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a group read only memory region and verify that it is only // mappable by members of the same group. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0040)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setuid(SHM_ACCESS_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Drop the CAP_IPC_OWNER capability and attempt to with the readonly // flag. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Attempt to map as read / write (should fail). // Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Attempt to map as execute (should fail). // Address = LxtShmAt(Id, NULL, SHM_EXEC); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create another read only memory region and verify that it is // mappable. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0004)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(setuid(SHM_ACCESS_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Remove all group membership, drop the CAP_IPC_OWNER capability, and // attempt to with the readonly flag. // LxtCheckErrno(Result = setgroups(0, NULL)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Attempt to map as read / write (should fail). // Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Attempt to map as execute (should fail). // Address = LxtShmAt(Id, NULL, SHM_EXEC); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region that is write only This should be // unmappable by processes without the CAP_IPC_OWNER capability. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0222)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Drop the CAP_IPC_OWNER capability and attempt to map again (should fail). // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); Address = LxtShmAt(Id, NULL, SHM_RDONLY); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Attempt to map as execute (should fail). // Address = LxtShmAt(Id, NULL, SHM_EXEC); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region that can only be read or written by the // owner. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0700)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Drop the CAP_IPC_OWNER capability and attempt to map (should fail). // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(SHM_ACCESS_GID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Change the UID and verify the mapping fails. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); Address = LxtShmAt(Id, NULL, SHM_RDONLY); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region that is only mappable by other. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0007)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Verify the region is mappable with CAP_IPC_OWNER. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; // // Drop the CAP_IPC_OWNER capability and attempt to map again this // should fail because the caller is still has a matching UID. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); Address = LxtShmAt(Id, NULL, SHM_RDONLY); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Change the UID and attempt to map, this should still fail because // the caller has group ownership. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); Address = LxtShmAt(Id, NULL, SHM_RDONLY); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Change the caller GID and attempt to map, this should still fail // because the caller has a supplimentaty group membership. // LxtCheckErrno(setgid(SHM_ACCESS_GID)); Address = LxtShmAt(Id, NULL, SHM_RDONLY); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Drop supplimentary group membership, finally this should succeed. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_EXEC)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY | SHM_EXEC)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; goto ErrorExit; } // // Create a shared memory region that is only mappable as read / execute. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0555)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Drop the CAP_IPC_OWNER capability and try to map read / write and // read / write / execute (should fail). // 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)); Address = LxtShmAt(Id, NULL, 0); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } Address = LxtShmAt(Id, NULL, SHM_EXEC); if (Address != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("Unexpectedly able to shmat"); goto ErrorExit; } // // Map the region as readonly, read / execute. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, SHM_RDONLY | SHM_EXEC)); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; ErrorExit: if (Address != NULL) { LxtShmDt(Address); } if (ChildPid == 0) { _exit(Result); } // // N.B. The identifier should not be removed by any child processes. // if (Id != -1) { LxtShmCtl(Id, IPC_RMID, NULL); } return Result; } int ShmAtDtSyscall(PLXT_ARGS Args) { unsigned char* Address; unsigned char* Address2; int ChildPid; int Id; key_t Key; void* MapResult; struct shmid_ds ParentStat; unsigned char* RemappedMemory; size_t Result; struct shmid_ds Stat; Address = NULL; Address2 = NULL; ChildPid = -1; Id = -1; // // (1) Create a shared memory region. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0)); LxtLogInfo("Id = %d", Id); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat)); LxtCheckEqual(PAGE_SIZE * 3, ParentStat.shm_segsz, "%Iu"); LxtCheckEqual(0, ParentStat.shm_atime, "%Iu"); LxtCheckEqual(0, ParentStat.shm_dtime, "%Iu"); LxtCheckNotEqual(0, ParentStat.shm_ctime, "%Iu"); LxtCheckEqual(ParentStat.shm_nattch, 0, "%Iu"); // // Map the shared memory region. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat)); ShmPrintInfoAttach(&ParentStat); LxtCheckNotEqual(0, ParentStat.shm_atime, "%Iu"); LxtCheckEqual(0, ParentStat.shm_dtime, "%Iu"); LxtCheckEqual(ParentStat.shm_nattch, 1, "%Iu"); LxtCheckEqual(getpid(), ParentStat.shm_lpid, "%Iu"); // // Sleep for 2 seconds then fork and verify that attach statics are // updated correctly. The attach count and attach time should be updated // but the last attach pid should not change. // sleep(2); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); LxtCheckEqual(ParentStat.shm_lpid, Stat.shm_lpid, "%Iu"); LxtCheckNotEqual(ParentStat.shm_atime, Stat.shm_atime, "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckNotEqual(0, Stat.shm_dtime, "%Iu"); // // Attempt to map the region in an area that already is mapped. // Address2 = LxtShmAt(Id, Address, 0); if (Address2 != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("shmat on a used region should fail without SHM_REMAP flag %p %d", Address2, errno); goto ErrorExit; } Address2 = LxtShmAt(Id, Address + PAGE_SIZE, 0); if (Address2 != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("shmat on a used region should fail without SHM_REMAP flag %p %d", Address2, errno); goto ErrorExit; } Address2 = LxtShmAt(Id, Address + (PAGE_SIZE * 2), 0); if (Address2 != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("shmat on a used region should fail without SHM_REMAP flag %p %d", Address2, errno); goto ErrorExit; } if (g_RunningOnNative == false) { LxtLogInfo("WARNING: these variations are expected to fail on native Ubuntu"); Address2 = LxtShmAt(Id, Address, SHM_REMAP); if (Address2 != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("shmat with SHM_REMAP replacing entire region"); goto ErrorExit; } Address2 = LxtShmAt(Id, Address + PAGE_SIZE, SHM_REMAP); if (Address2 != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("shmat with SHM_REMAP replacing last two pages."); goto ErrorExit; } // // Unmap the first page in the range. // LxtCheckErrnoFailure(munmap(Address, PAGE_SIZE), EINVAL); // // Unmap the middle page of the three-page range. // LxtCheckErrnoFailure(munmap(Address + PAGE_SIZE, PAGE_SIZE), EINVAL); // // Unmap the last page in the range. // LxtCheckErrnoFailure(munmap(Address + (2 * PAGE_SIZE), PAGE_SIZE), EINVAL); // // Use the remap system call to resize the region. // RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL); if (RemappedMemory != MAP_FAILED) { Result = LXT_RESULT_FAILURE; LxtLogError("mremap moving the region."); goto ErrorExit; } goto ErrorExit; } // // Use the SHM_REMAP flag to replace the entire region. // LxtCheckMapErrno(Address2 = LxtShmAt(Id, Address, SHM_REMAP)); LxtCheckEqual(Address, Address2, "%p"); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckErrno(LxtShmDt(Address)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL); // // Use the SHM_REMAP flag to replace the last two pages of the original // region. // LxtCheckMapErrno(Address = LxtShmAt(Id, Address, 0)); LxtCheckMapErrno(Address2 = LxtShmAt(Id, Address + PAGE_SIZE, SHM_REMAP)); LxtCheckEqual(Address + PAGE_SIZE, Address2, "%p"); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); LxtCheckErrno(LxtShmDt(Address)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckErrno(LxtShmDt(Address2)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL); LxtCheckErrnoFailure(LxtShmDt(Address2), EINVAL); Address = NULL; Address2 = NULL; // // Unmap the middle page of the three-page range to split the region, // this should increment the attach count. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(munmap(Address + PAGE_SIZE, PAGE_SIZE)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); LxtCheckNotEqual(0, Stat.shm_dtime, "%Iu"); // // Unmap the last page in the range. // LxtCheckErrno(munmap(Address + (2 * PAGE_SIZE), PAGE_SIZE)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); // // Use detatch to clear the range. // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); // // (2) Map the region again. Unmap the middle page of the three-page range. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckErrno(munmap(Address + PAGE_SIZE, PAGE_SIZE)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); // // Use shmdt to remove both remaining mapped regions, this should unmap // both attached regions. // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); // // (3) Use the remap system call to resize the region. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckMapErrno(RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL)); LxtLogInfo("RemappedMemory = %p", RemappedMemory); // // If the address changed, attempt to remap the old address. // if (Address != RemappedMemory) { LxtCheckErrnoFailure(LxtShmDt(Address), EINVAL); Address = RemappedMemory; } LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); // // Unmap the middle two pages in the range. // LxtCheckErrno(munmap(Address + PAGE_SIZE, (2 * PAGE_SIZE))); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); // // Unmap the first page in the range. // LxtCheckErrno(munmap(Address, PAGE_SIZE)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); // // Use shmdt to remove the remaining region (the last page in the range). // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); // // (4) Map the region again. Use the mremap system call to shrink the // region and validate that the global shared memory region remains the // same size. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckEqual(Stat.shm_segsz, (PAGE_SIZE * 3), "%Iu"); LxtCheckMapErrno(RemappedMemory = LxtMremap(Address, PAGE_SIZE * 3, PAGE_SIZE, 0, NULL)); LxtLogInfo("RemappedMemory = %p", RemappedMemory); LxtCheckEqual(Address, RemappedMemory, "%p"); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckEqual(Stat.shm_segsz, PAGE_SIZE * 3, "%Iu"); // // Use shmdt to remove the region. // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); // // (5) Map the region twice. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address2 = %p", Address2); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); // // Ensure the shared memory region were mapped to different locations and // detach both. // LxtCheckNotEqual(Address, Address2, "%p"); LxtCheckErrno(LxtShmDt(Address2)); Address2 = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); // // (6) Map the region, delete the region, and validate that the region is // still able to be mapped. // LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); // // Delete the region again (should succeed). // LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); Address[0] = 'a'; LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); Address2[0] = 'a'; ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); // // Detatch both mapped regions. // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfoAttach(&Stat); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); LxtCheckErrno(LxtShmDt(Address2)); Address2 = NULL; // // The region should be deleted at this point to the shmctl should fail. // LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EINVAL); Id = -1; // // (7) Delete the shared memory region and attempt to attach it afterwards. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0)); LxtLogInfo("Id = %d", Id); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Address = LxtShmAt(Id, NULL, 0); if ((Address != MAP_FAILED) && (errno != EINVAL)) { Result = LXT_RESULT_FAILURE; LxtLogError("unexpectedly able to attach deleted memory region %p, %d", Address, errno); goto ErrorExit; } // // Attempt to stat the deleted region. // LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EINVAL); Id = -1; // // (8) Use mremap to move the last page to a new location. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE * 3, 0)); LxtLogInfo("Id = %d", Id); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtLogInfo("Address = %p", Address); LxtCheckMapErrno(Address2 = LxtMremap(Address + (2 * PAGE_SIZE), PAGE_SIZE, PAGE_SIZE * 4, MREMAP_MAYMOVE, NULL)); LxtLogInfo("Address2 = %p", Address2); LxtCheckNotEqual(Address + (2 * PAGE_SIZE), Address2, "%p"); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 2, "%Iu"); // // Detatch the original address and validate the second region remains. // LxtCheckErrno(LxtShmDt(Address)); Address = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); // // Ensure that shmdt does not work for the new address. // LxtCheckErrnoFailure(LxtShmDt(Address2), EINVAL); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 1, "%Iu"); // // Call shmdt on what would have been the start of the new region. // // N.B. This functions like a new mapping of the memory where the first two // pages have been unmapped. // LxtCheckErrno(LxtShmDt(Address2 - (2 * PAGE_SIZE))); Address2 = NULL; LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_nattch, 0, "%Iu"); ErrorExit: if (Address != NULL) { LxtShmDt(Address); } if (Address2 != NULL) { LxtShmDt(Address2); } if (ChildPid == 0) { _exit(Result); } if (Id != -1) { LxtShmCtl(Id, IPC_RMID, NULL); } return Result; } int ShmGetAccess(PLXT_ARGS Args) { struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int Id; key_t Key; int Mode; int Result; ChildPid = -1; Id = -1; LxtCheckErrno(LxtGetrandom(&Key, sizeof(Key), 0)); LxtLogInfo("Key = %u", Key); // // Create a shared memory region with a mode of all zeros. // Mode = 0000; LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); // // Drop the CAP_IPC_OWNER capability. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the UID. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the GID. // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Drop supplimentary group membership. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region with a user read / write / execute mode. // Mode = 0700; LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); // // Drop the CAP_IPC_OWNER capability. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the UID. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the GID. // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Drop supplimentary group membership. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region with a group read / write / execute mode. // Mode = 0070; LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); // // Drop the CAP_IPC_OWNER capability. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the UID (group still matches so this should succeed). // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the GID (callers still has supplimentary group membership so // this should succeed). // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Drop supplimentary group membership. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region with a other read / write / execute mode. // Mode = 0007; LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); // // Drop the CAP_IPC_OWNER capability. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the UID. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the GID (callers still has supplimentary group membership so // this should succeed). // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Drop supplimentary group membership (this should succeed). // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0700), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0070), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0007), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0124), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; // // Create a shared memory region with a other read / write mode. // Mode = 0006; LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); // // Drop the CAP_IPC_OWNER capability. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the UID. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Change the GID (callers still has supplimentary group membership so // this should succeed). // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0004), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0002), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0001), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); // // Drop supplimentary group membership (this should succeed). // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0700), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0070), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0007), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0124), EACCES); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0666), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0600), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0060), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0006), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0024), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0424), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0024), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0000), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); Id = -1; ErrorExit: if (ChildPid == 0) { _exit(Result); } // // N.B. The identifier should not be removed by any child processes. // if (Id != -1) { LxtShmCtl(Id, IPC_RMID, NULL); } return Result; } int ShmGetSyscall(PLXT_ARGS Args) { struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int Id; key_t Key; int Mode; size_t Result; struct shmid_ds Stat; time_t Time; ChildPid = -1; Id = -1; // // Create a key, verify that creating the key with the IPC_EXCL flag fails. // Mode = 0000; LxtLogInfo("Mode %o", Mode); LxtCheckErrno(LxtGetrandom(&Key, sizeof(Key), 0)); LxtLogInfo("Key = %u", Key); LxtCheckErrno(Id = LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL | Mode))); LxtLogInfo("Id = %d", Id); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); ShmPrintInfo(&Stat); LxtCheckEqual(Key, Stat.shm_perm.__key, "%Iu"); LxtCheckEqual(PAGE_SIZE, Stat.shm_segsz, "%Iu"); LxtCheckEqual(getpid(), Stat.shm_cpid, "%Iu"); LxtCheckEqual(0, Stat.shm_lpid, "%Iu"); LxtCheckEqual(0, Stat.shm_atime, "%Iu"); LxtCheckEqual(0, Stat.shm_dtime, "%Iu"); LxtCheckNotEqual(0, Stat.shm_ctime, "%Iu"); LxtCheckEqual(Mode, Stat.shm_perm.mode, "%o"); LxtCheckEqual(getuid(), Stat.shm_perm.cuid, "%d"); LxtCheckEqual(getuid(), Stat.shm_perm.uid, "%d"); LxtCheckEqual(getgid(), Stat.shm_perm.cgid, "%d"); LxtCheckEqual(getgid(), Stat.shm_perm.gid, "%d"); // // shmget with IPC_CREAT or IPC_EXCL when the region already exists. // LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), "%Iu"); // // Create a child with a different uid and gid that does not have the // IPC_OWNER capability. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(setuid(SHM_ACCESS_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); // // These should succeed because the child still has the IPC_OWNER cap. // LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0777), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0666), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0600), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0060), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0006), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), "%Iu"); // // Drop all group membership and the CAP_IPC_OWNER capability and // attempt to call shmget with unmatching mode bits. // LxtCheckErrno(Result = setgroups(0, NULL)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0777), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0666), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0600), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0060), EACCES); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, 0006), EACCES); // // Use the same permission as before, these should succeed. // LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_CREAT), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, IPC_EXCL), "%Iu"); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, 0), "%Iu"); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // shmget with size = 0 should succeed. // LxtCheckEqual(Id, LxtShmGet(Key, 0, 0), "%Iu"); // // Invalid parameter variations. // // // shmget with IPC_CREAT | IPC_EXCL when the region already exists, should // succeed with only IPC_EXCL. // LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, (IPC_CREAT | IPC_EXCL)), EEXIST); // // shmget with a known key and a size that does not match. // LxtCheckErrnoFailure(LxtShmGet(Key, (PAGE_SIZE * 2), 0), EINVAL); LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE + 1, 0), EINVAL); // // N.B. There appears to be no error checking for invalid flags, only the // presence of valid flags. // // -1 includes the IPC_EXCL flag so this shoudl return EEXIST. // LxtCheckErrnoFailure(LxtShmGet(Key, PAGE_SIZE, -1), EEXIST); LxtCheckEqual(Id, LxtShmGet(Key, PAGE_SIZE, (-1 & ~IPC_EXCL)), "%Iu"); // // Delete the region and create a new one with a size of one byte. // LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, &Stat)); Id = -1; LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, 1, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(1, Stat.shm_segsz, "%Iu"); // // Delete the region and create a new region with a size of zero bytes // (should fail). // LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, &Stat)); Id = -1; LxtCheckErrnoFailure(Id = LxtShmGet(IPC_PRIVATE, 0, 0), EINVAL); ErrorExit: if (ChildPid == 0) { _exit(Result); } if (Id != -1) { LxtShmCtl(Id, IPC_RMID, &Stat); } return Result; } int ShmCtlSyscall(PLXT_ARGS Args) { struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int Id; struct shminfo IpcInfo; key_t Key; struct shmid_ds OldStat; int RandomId; size_t Result; struct shm_info ShmInfo; struct shmid_ds Stat = {0}; Id = -1; // // Test permissions for the IPC_STAT. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); // // Drop the CAP_IPC_OWNER capability and verify that the region cannot // be queried. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES); // // Create a no access shared memory region and verify that it cannot be // queried without the CAP_IPC_OWNER (even by its owner). // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0)); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); // // Create a write only shared memory region and verify that it cannot be // queried without the CAP_IPC_OWNER (even by its owner). // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0200)); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); // // Create a read only shared memory region and verify that it can be // queried. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0400)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Test permissions for IPC_SET. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Drop the CAP_IPC_OWNER capability and verify that IPC_SET can still // be called by the owner. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the GID. // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Drop supplimentary group membership. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the UID (this should fail). // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, NULL), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EPERM); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Test permissions for IPC_SET. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First attempt with the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[CAP_TO_INDEX(CAP_IPC_OWNER)].permitted |= CAP_TO_MASK(CAP_IPC_OWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Drop the CAP_IPC_OWNER capability and verify that IPC_SET can still // be called by the creator and owner. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the owner UID. // Stat.shm_perm.uid = SHM_ACCESS_UID; LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the GID. // LxtCheckErrno(setgid(SHM_ACCESS_GID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Drop supplimentary group membership. // LxtCheckErrno(Result = setgroups(0, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the UID to match (this should succeed). // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // IPC_STAT should still fail. // LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, &Stat), EACCES); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Reset the region's UID. // Stat.shm_perm.uid = getuid(); LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Test permissions for SHM_LOCK / SHM_UNLOCK. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Drop the CAP_IPC_LOCK capability. // memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapGet(&CapHeader, CapData)) LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); CapData[CAP_TO_INDEX(CAP_IPC_LOCK)].permitted &= ~CAP_TO_MASK(CAP_IPC_LOCK); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); // // Change the UID and verify SHM_LOCK and SHM_UNLOCK fail. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_LOCK, NULL), EPERM); LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_UNLOCK, NULL), EPERM); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Test permissions for IPC_RMID. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Drop the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); // // Change the UID and verify IPC_RMID fails. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_RMID, NULL), EPERM); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Verify IPC_RMID can be called by the memory region's owner. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // Drop the CAP_IPC_OWNER capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); CapData[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); // // Change the owner UID. // Stat.shm_perm.uid = SHM_ACCESS_UID; LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); // // Change the caller's UID to match. // LxtCheckErrno(setuid(SHM_ACCESS_UID)); LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(LxtShmCtl(Id, IPC_RMID, NULL)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Create a new shared memory region since the previous was just deleted. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0)); // // Verify IPC_INFO. // LxtCheckErrno(LxtShmCtl(Id, IPC_INFO, &IpcInfo)); LxtCheckErrno(LxtShmCtl(0, IPC_INFO, &IpcInfo)); LxtLogInfo("shminfo.shmmax %Iu", IpcInfo.shmmax); LxtLogInfo("shminfo.shmmin %Iu", IpcInfo.shmmin); LxtLogInfo("shminfo.shmmni %Iu", IpcInfo.shmmni); LxtLogInfo("shminfo.shmseg %Iu", IpcInfo.shmseg); LxtLogInfo("shminfo.shmall %Iu", IpcInfo.shmall); LxtCheckEqual(IpcInfo.shmmin, 1, "%Iu"); // // Verify SHM_INFO. // LxtCheckErrno(LxtShmCtl(Id, SHM_INFO, &ShmInfo)); LxtCheckErrno(LxtShmCtl(0, SHM_INFO, &ShmInfo)); LxtLogInfo("shm_info.used_ids %Iu", ShmInfo.used_ids); LxtLogInfo("shm_info.shm_tot %Iu", ShmInfo.shm_tot); LxtLogInfo("shm_info.shm_rss %Iu", ShmInfo.shm_rss); LxtLogInfo("shm_info.shm_swp %Iu", ShmInfo.shm_swp); LxtLogInfo("shm_info.swap_attempts %Iu", ShmInfo.swap_attempts); LxtLogInfo("shm_info.swap_successes %Iu", ShmInfo.swap_successes); LxtCheckNotEqual(ShmInfo.used_ids, 0, "%Iu"); // // Verify SHM_LOCK and SHM_UNLOCK. The locked state is boolean (there is // no count for locked / unlocked). // LxtCheckErrno(LxtShmCtl(Id, SHM_LOCK, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(SHM_LOCKED, Stat.shm_perm.mode & SHM_LOCKED, "%o"); LxtCheckErrno(LxtShmCtl(Id, SHM_LOCK, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(SHM_LOCKED, Stat.shm_perm.mode & SHM_LOCKED, "%o"); LxtCheckErrno(LxtShmCtl(Id, SHM_UNLOCK, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(0, Stat.shm_perm.mode & SHM_LOCKED, "%o"); LxtCheckErrno(LxtShmCtl(Id, SHM_UNLOCK, NULL)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(0, Stat.shm_perm.mode & SHM_LOCKED, "%o"); // // Invalid parameter variations. // // // Ensure IPC_SET cannot set invalid mode bits (they are silently ignored). // LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); Stat.shm_perm.mode = -1; LxtCheckErrno(LxtShmCtl(Id, IPC_SET, &Stat)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_perm.mode, 0777, "%o"); // // Ensure the uid and gid cannot be set to -1. // LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &OldStat)); Stat = OldStat; Stat.shm_perm.uid = -1; LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EINVAL); Stat = OldStat; Stat.shm_perm.gid = -1; LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, &Stat), EINVAL); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(Stat.shm_perm.uid, OldStat.shm_perm.uid, "%d"); LxtCheckEqual(Stat.shm_perm.gid, OldStat.shm_perm.gid, "%d"); LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_STAT, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, NULL), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_STAT, -1), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_SET, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, NULL), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_SET, -1), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(-1, IPC_INFO, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_INFO, NULL), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(Id, IPC_INFO, -1), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_INFO, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_INFO, NULL), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(Id, SHM_INFO, -1), EFAULT); LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_LOCK, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(-1, SHM_UNLOCK, NULL), EINVAL); // // Generate an ID that does not refer to a valid memory region and attempt // operations on the non-existent region. // do { LxtCheckErrno(LxtGetrandom(&RandomId, sizeof(RandomId), 0)); Result = LxtShmCtl(RandomId, IPC_STAT, &Stat); } while ((Result == 0) && (errno != EINVAL)); LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_RMID, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_STAT, &Stat), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(RandomId, IPC_SET, &Stat), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(RandomId, SHM_LOCK, NULL), EINVAL); LxtCheckErrnoFailure(LxtShmCtl(RandomId, SHM_UNLOCK, NULL), EINVAL); ErrorExit: if (ChildPid == 0) { _exit(Result); } if (Id != -1) { LxtShmCtl(Id, IPC_RMID, NULL); } return Result; } int ShmPidNamespaceWork(void) /*++ Routine Description: This routine tests the behavior of System V shared memory across IPC namespaces. A child threadgroup is forked into a new IPC namespace, and the parent and child communicate across a unix socket connection. Each side queries the credentials of the other side via SO_PEERCRED and ancillary messages and validates that the appriate credentials are returned. Arguments: None. Return Value: 0 on success, -1 on failure. --*/ { void* Address; void* Address2; pid_t ChildPid = 0; int Id; void* MapResult; pid_t ParentPid; struct shmid_ds ParentStat; int Result; struct shmid_ds Stat; int Status; Address = NULL; Address2 = NULL; Id = -1; LXT_SYNCHRONIZATION_POINT_START(); // // Create and map a shared memory region. // LxtCheckErrno(Id = LxtShmGet(IPC_PRIVATE, PAGE_SIZE, 0)); LxtCheckMapErrno(Address = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &ParentStat)); // // Unshare the PID namespace used for children. // LxtLogInfo("Unsharing CLONE_NEWPID"); LxtCheckErrno(unshare(CLONE_NEWPID)); // // Fork a child that will exist in a new IPC namespace. // ParentPid = getpid(); LxtLogInfo("ParentPid %d", ParentPid); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtLogInfo("Child's view of ChildPid %d", getpid()); // // Attatch the shared segment. // LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(getpid(), Stat.shm_lpid, "%d"); LXT_SYNCHRONIZATION_POINT(); // // Wait for the parent to query credentials. // LXT_SYNCHRONIZATION_POINT(); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(ParentPid, Stat.shm_lpid, "%d"); } else { LxtLogInfo("Parent's view of ChildPid %d", ChildPid); // // Wait for the child to attach. // LXT_SYNCHRONIZATION_POINT(); // // Query the last attach pid (should NOT match ChildPid) and create // a new mapping. // LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckNotEqual(ChildPid, Stat.shm_lpid, "%d"); LxtCheckMapErrno(Address2 = LxtShmAt(Id, NULL, 0)); LxtCheckErrno(LxtShmCtl(Id, IPC_STAT, &Stat)); LxtCheckEqual(getpid(), Stat.shm_lpid, "%d"); LXT_SYNCHRONIZATION_POINT(); } Result = LXT_RESULT_SUCCESS; ErrorExit: LXT_SYNCHRONIZATION_POINT_END(); if (Address != NULL) { LxtShmDt(Address); } if (Address2 != NULL) { LxtShmDt(Address2); } if (ChildPid == 0) { _exit(Result); } if (Id != -1) { LxtShmCtl(Id, IPC_RMID, NULL); } return Result; } int ShmPidNamespace(PLXT_ARGS Args) /*++ Routine Description: This routine tests the behavior of System V shared memory across IPC namespaces. Arguments: Args - Supplies the command line arguments. Return Value: 0 on success, -1 on failure. --*/ { pid_t ChildPid; int Result; // // Fork into a new parent so that the existing threadgroup does not have its // IPC namespaces altered for later tests. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { _exit(ShmPidNamespaceWork()); } LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); Result = LXT_RESULT_SUCCESS; ErrorExit: return Result; } void ShmPrintInfo(struct shmid_ds* Stat) { if (g_VerboseShm == false) { return; } LxtLogInfo("shm_perm.__key %u", Stat->shm_perm.__key); LxtLogInfo("shm_perm.uid %u", Stat->shm_perm.uid); LxtLogInfo("shm_perm.gid %u", Stat->shm_perm.gid); LxtLogInfo("shm_perm.cuid %u", Stat->shm_perm.cuid); LxtLogInfo("shm_perm.cgid %u", Stat->shm_perm.cgid); LxtLogInfo("shm_perm.mode %o", Stat->shm_perm.mode); LxtLogInfo("shm_perm.__seq %d", Stat->shm_perm.__seq); LxtLogInfo("shm_segsz %Iu", Stat->shm_segsz); LxtLogInfo("shm_atime %Iu", Stat->shm_atime); LxtLogInfo("shm_dtime %Iu", Stat->shm_dtime); LxtLogInfo("shm_ctime %Iu", Stat->shm_ctime); LxtLogInfo("shm_cpid %Iu", Stat->shm_cpid); LxtLogInfo("shm_lpid %Iu", Stat->shm_lpid); LxtLogInfo("shm_nattch %Iu", Stat->shm_nattch); return; } void ShmPrintInfoAttach(struct shmid_ds* Stat) { if (g_VerboseShm == false) { return; } LxtLogInfo("shm_segsz %Iu", Stat->shm_segsz); LxtLogInfo("shm_atime %Iu", Stat->shm_atime); LxtLogInfo("shm_dtime %Iu", Stat->shm_dtime); LxtLogInfo("shm_ctime %Iu", Stat->shm_ctime); LxtLogInfo("shm_cpid %Iu", Stat->shm_cpid); LxtLogInfo("shm_lpid %Iu", Stat->shm_lpid); LxtLogInfo("shm_nattch %Iu", Stat->shm_nattch); return; }