mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 07:23:20 +00:00

Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016. The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021: Number of commits on the main branch: 2930 Number of contributors: 31 Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2625 lines
84 KiB
C
2625 lines
84 KiB
C
/*++
|
|
|
|
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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/xattr.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/wait.h>
|
|
#include <grp.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <time.h>
|
|
#include <linux/random.h>
|
|
|
|
#if !defined(__amd64__) && !defined(__aarch64__)
|
|
|
|
#include <sys/capability.h>
|
|
|
|
#else
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <linux/capability.h>
|
|
|
|
#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;
|
|
}
|