We have a set of applications which use a shared memory
area to provide status information to a managing service
running on the same machine, and to a managing application
running on another machine via SNMP. The management service,
each of our applications, and our SNMP extension agent
all include code to create a certain shared memory area if
it doesn't exist yet, or find it and map it if it does
already exist. Each application has certain variables
in the shared memory area that it writes to, while our
SNMP extension agent reads the shared memory area
and makes its contents available to remote apps for querying.
All this has been working fine on Windows NT and Windows 2000.
We recently started testing this code for the first time
on Windows 2003, and find that:
1. The management service gets one "shared" memory area.
2. The SNMP extension agent gets another "shared" memory area.
3. Each application which is started from a command prompt
gets the same shared memory area, different from those
obtained by #1 and #2.
The result is that the SNMP agent can't see the shared
memory that's being written to by the other service
and the applications, and our management app on the other
machine can't tell that any apps are running or what their
status is. It also can't tell that the management service
is running.
Both the SNMP Service and our management service are running
as a domain user - the same user who is logged in and starting
applications from the command prompt (or shortcut links, etc.).
The problem code is:
BOOL fInit;
// Set the access control list to everyone accessible.
SECURITY_INFORMATION SecurityInformation;
BYTE byACLBuffer[4096];
DWORD swACLSize = sizeof(byACLBuffer);
SECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_ATTRIBUTES SecurityAttributes;
BYTE bySIDBuffer[1024];
DWORD dwSIDSize = sizeof(bySIDBuffer);
SecurityInformation = DACL_SECURITY_INFORMATION;
InitializeSecurityDescriptor(&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION);
InitializeAcl((PACL)byACLBuffer, sizeof(byACLBuffer), ACL_REVISION);
if (NameToSID("Everyone", (PSID) bySIDBuffer, &dwSIDSize)) {
AddAccessAllowedAce((PACL) byACLBuffer, ACL_REVISION, GENERIC_ALL,
bySIDBuffer);
}
if (!SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, (PACL)
byACLBuffer, FALSE)) {
;
}
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
SecurityAttributes.lpSecurityDescriptor = & SecurityDescriptor;
SecurityAttributes.bInheritHandle = FALSE;
// Create a named file mapping object.
// Using the above security attribute variable.
hMapObject = CreateFileMapping(
(HANDLE) 0xFFFFFFFF, // use paging file
&SecurityAttributes,
PAGE_READWRITE, // read/write access
0, // size: high 32-bits
DBB_PERF_SHMEMSIZE, // size: low 32-bits
DBB_PERF_MEM_OBJ_NAME ); // name of map object
m_LastError = ::GetLastError();
// The first process to attach initializes memory.
fInit = ( m_LastError != ERROR_ALREADY_EXISTS);
// Get a pointer to the file-mapped shared memory.
if (hMapObject != NULL) {
lpvMem = MapViewOfFile(
hMapObject, // object to map view of
FILE_MAP_WRITE, // read/write access
0, // high offset: map from
0, // low offset: beginning
0); // default: map entire file
}
if ( hMapObject ) {
writelog( "got map object" );
}
if ( lpvMem ) {
writelog( "got mapped view of file" );
}
if (hMapObject == NULL || lpvMem == NULL) {
lpvMem = malloc( DBB_PERF_SHMEMSIZE );
fInit = true;
writelog("Get Shared memory failed, using heap memory\n");
}
// Initialize memory if this is the first process.
bool bfirst = true;
bool useHeap = false;
if (fInit) {
memset(lpvMem, '\0', DBB_PERF_SHMEMSIZE);
}
else {
SharedTableHeader * pHeader = (SharedTableHeader *) lpvMem;
if (pHeader->SharedMemorySize != DBB_PERF_SHMEMSIZE) {
writelog( "shared memory is wrong size so use heap" );
useHeap = true;
}
}
HANDLE hMutex = ::CreateMutex(&SecurityAttributes, false,
"DBB_PERF_MEMORY_CLEAR_MUTEX");
assert(hMutex);
if (!useHeap && !fInit) {
if (::GetLastError() != ERROR_ALREADY_EXISTS){
memset(lpvMem, '\0', DBB_PERF_SHMEMSIZE);
}
else {
bfirst = false;
SMemRegion = (SharedMemoryRegion *) lpvMem;
if (SMemRegion->header.SharedMemorySize != DBB_PERF_SHMEMSIZE ||
SMemRegion->header.HeaderSize != sizeof(SharedTableHeader) ||
SMemRegion->header.PerfSize != sizeof(dbbPerf) ||
SMemRegion->header.TraceSize != sizeof(dbbTrace))
{
writelog( "set useHeap because one or more size vars don't match" );
useHeap = true;
}
}
}
if (useHeap) {
writelog( "useHeap [2]" );
lpvMem = malloc( DBB_PERF_SHMEMSIZE );
memset(lpvMem, '\0', DBB_PERF_SHMEMSIZE);
bfirst = true;
MessageBox(NULL, "Instance versions are different, can't use shared
memory.",
"Version Conflict", MB_ICONWARNING | MB_OK);
}
SMemRegion = new (lpvMem) SharedMemoryRegion();
m_pTheOne = &(SMemRegion->perf);
m_pTheOne->m_bNotFirst = !bfirst;
// The snmp variables will be reset by the 'memset' above.
// ..............
static inline BOOL NameToSID(LPTSTR lpName, PSID pSID, LPDWORD dd)
{
TCHAR szDomain[256];
DWORD dwDomainSize = sizeof(szDomain);
SID_NAME_USE SidNameUse;
if (!LookupAccountName(NULL, lpName, pSID, dd, szDomain,
&dwDomainSize, &SidNameUse))
{
return FALSE;
}
return TRUE;
}
Jim Henry <jimhenry@pobox.com> writes:
Almost certainly (I don't have immediate access to up-to-date docs for
CreateFileMapping()), this is caused by each "pool" of processes (1, 2,
3 in your list above) existing in a different <something> (Terminal
Server session, Window Station, something else), and each <something>
having its own kernel object name space. For Terminal Server sessions,
you can use Global\name instead of name to put the name in the global
name space.
Use GetVersionEx and other items to determine whether the Global\name
notation will be necessary, and only use it if so, as, in the absence of
separate name spaces, backslashes are not allowed in kernel object
names.
HTH
--
SteveR
(throw away the dustbin, send to stever@... instead)
Humans are way too stupid to be dumb animals.
http://www.accidentalcreditor.org.uk/