CreateFileMapping, MapViewOfFile, handle leaking c++

14k Views Asked by At

Background: I am trying to create a memory mapped file that can be accessed by multiple processes. In the below code I only put in the code that pertains to the question I currently have to make things simpler. According to msdn I should be able to create a filemap, map the view of the file and close the handle I received from CreateFileMapping and the MapViewOfFile will keep my FileMap alive. The FileMap should be still accessible until I UnmapViewOfFile.

MSDN: CreateFileMapping function

Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.

Problem: After successfully mapping the view of the file and then closing the handle received by CreateFileMapping the FileMap no longer exists (it should still exist) and my MemMapFileReader is able to create a new map with error of 0. (When it should be receiving error 183 'already exists')

Bad Solution: Not closing the handle allows it to be accessed by the MemMapFileReader program but causes a handle leak in the MemMapFileCreator because the handle is never closed until the process is closed.

Question: What am I missing or doing wrong?

MemMapFileCreator

#include "stdafx.h"


#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
TCHAR szMsg[] = TEXT("Message from first process.");

int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    BUF_SIZE,                // maximum object size (low-order DWORD)
    szName);                 // name of mapping object

DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    std::cin.get();
    return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile,   // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    std::cin.get();
    return 1;
}


CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));

CloseHandle(hMapFile);

_getch();


UnmapViewOfFile(pBuf);
return 0;
}

MemMapFileReader

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");

int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,
    NULL,
    PAGE_READWRITE,   // read/write access
    0,
    BUF_SIZE,
    szName);               // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not open file mapping object (%d).\n"),
        GetLastError());
    std::cin.get();
    return 1;
}

pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
    FILE_MAP_ALL_ACCESS,  // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    std::cin.get();
    return 1;
}

MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);

UnmapViewOfFile(pBuf);

CloseHandle(hMapFile);

std::cin.get();
return 0;
}
5

There are 5 best solutions below

6
On BEST ANSWER

From MSDN:

Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released. Therefore, to fully close a file mapping object, an application must unmap all mapped views of the file mapping object by calling UnmapViewOfFile and close the file mapping object handle by calling CloseHandle. These functions can be called in any order.

The CreateFileMapping documentation says that to fully close a file all handles must be closed and the order does not matter. This logic is not invertible: you cannot close a handle and expect to use the other handles as if the file mapping wasn't 'closed'.

In other words, it means that to clean up the file mapping you need to close all handles in any order. However, you can not close the underlying file-mapping object and still use the views that depend on it.

0
On

Looking at your description, it seems that you want your Creator app to close before/irrespective of any client. This will have following things: As soon as your creator app closes, Windows will automatically close any Handles opened by that process because this is a resource leak prevention mechanism. This means, if no client is attached to the just created file mapping object creator is the only app referring it. When this app closes, all the handles referring the file mapping object are closed which in turn will trigger Windows rule that "When all the handles to a File Mapping Object are closed the object will be automatically freed" causing your object to be removed!! This is the reason your client apps get error 0.

Approach 1:

Motivation to this approach: You want to write one application and multiple instances of same app will communicate via shared memory.

In such apps involving shared memory for inter-process communication, the only difference between the two apps that we develop boils down to just "who creates the object", rest of the operations are to use the shared memory and are same for both the apps. So we can avoid developing two apps and instead write only one.

Another motivation for this approach is that, in client-server LIKE architecture it is mandatory that server starts before client. If you do not want to have this restriction then this approach will help. CAUTION, this means you need to think of changing the architecture and this can be quiet a task for existing apps!

Implementation:

// CreateFileMapping object
...
// Check the error value using GetLastError.
If (GetLastError() == ERROR_ALREADY_EXISTS)
{
    // I am a client
}
else
{
    // I am creator/server
}

In your case, as you are allowing your creator app to close before clients means the creator does not assume much big responsibility like a server. If that is the case then you can use above approach. It might save you of maintaining one more APP!

Approcah 2: Keep your app alive untill at least any one client connects. But again this will be a little bit difficult to implement!

Regarding UnmapViewOfFile This function is a proper way to close/release the system resources. BUT this does not mean that, closing an app without calling it will keep your resources ALIVE ! Not calling it means two cases:

  1. Application crashed.
  2. Developer forgot to call it!

This will cause resource leaks! Windows fault tolerance mechanism mitigates this issue by closing all the handles opened by a process when it ends either properly or abruptly. SO we can think of UnmapViewOfFile as a function to PROPERLY release resources.

3
On

after you close Section handle view in creator process not disappear, until unmaping bat it name - "MyFileMappingObject" in NT namespace is destroyed. as result next call to CreateFileMapping - not found named object "MyFileMappingObject" and create new (when mast by your logic open existing). again - section not destroyed, but it name is destroyed. what you name - Bad Solution - really not bad - this is absolute normal - you mast not close handle to Section. and this is not handle leak - simply will be permanent open handle in your process. this situation is absolute normal

you use is NAMED section. after close it handle - section NOT DESTROYED - because exist also view of section, which hold it. but NAME of section is DESTROYED. and new call to create section - not open existing but create new.

2
On

The CreateFileMapping() documentation says:

Mapped views of a file mapping object maintain internal references to the object, and a file mapping object does not close until all references to it are released.

The CloseHandle() documentation says:

In general, CloseHandle invalidates the specified object handle, decrements the object's handle count, and performs object retention checks. After the last handle to an object is closed, the object is removed from the system.

The mapped views simply keep the mapping object's reference count above zero until they are unmapped, but they do not keep the underlying file/mapping itself open.

0
On

exist else one solution, which show that you all wrong. it require SE_CREATE_PERMANENT_PRIVILEGE, but for demo this is normal

STATIC_OBJECT_ATTRIBUTES_EX(g_oa, "\\BaseNamedObjects\\MyFileMappingObject", OBJ_CASE_INSENSITIVE|OBJ_PERMANENT, 0, 0);

NTSTATUS CreateAndWrite()
{
    NTSTATUS status;
    HANDLE hSection;
    LARGE_INTEGER Size = { PAGE_SIZE };
    if (0 <= (status = ZwCreateSection(&hSection, SECTION_MAP_READ|SECTION_MAP_WRITE, &g_oa, &Size, PAGE_READWRITE, SEC_COMMIT, 0)))
    {
        PVOID BaseAddress = 0;
        SIZE_T ViewSize = 0;
        if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READWRITE)))
        {
            STATIC_WSTRING(szMsg, "Message from first process.");
            memcpy(BaseAddress, szMsg, sizeof(szMsg));
            ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
        }
        ZwClose(hSection);
    }

    return status;
}

NTSTATUS OpenReadAndDestroy()
{
    NTSTATUS status;
    HANDLE hSection;
    if (0 <= (status = ZwOpenSection(&hSection, SECTION_MAP_READ|DELETE, &g_oa)))
    {
        PVOID BaseAddress = 0;
        SIZE_T ViewSize = 0;
        if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY)))
        {
            MessageBox(0, (PCWSTR)BaseAddress, 0, 0);
            ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
        }

        ZwMakeTemporaryObject(hSection);

        ZwClose(hSection);
    }

    return status;
}

        if (0 <= GotPermanentPrivilege())
        {
      if (0 <= CreateAndWrite())
      {
        // at this point - no one handles for "MyFileMappingObject" exist
        // we close all and even unmap view
        // but 1 reference to section object exist (by OBJ_PERMANENT flag)
        // and NAME is still exist
        // as result we can open,map and read data :)
        OpenReadAndDestroy();
      }
        }

what you say for this ? section is destroyed, after all handle closed ?? cannot use views ? D)

p.s. SE_CREATE_PERMANENT_PRIVILEGE - usually have only LocalSystem, but we can got it, if have SE_DEBUG_PRIVILEGE+SE_IMPERSONATE_PRIVILEGE - open "system" thread and impersonate on it