2

I want to really understand when the ERROR_USER_MAPPED_FILE occurs. So I wrote some snippets. To reproduce the error. But it did not work. Please help me fix my code

Process 1:

    HANDLE hFile = CreateFile("C:\\test\\full.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return GetLastError();

    HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    if (hMapFile == INVALID_HANDLE_VALUE)
        return GetLastError();

    mapHandles.push_back(hMapFile);

    viewHandles.push_back(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0));

I am Halting the process and I have not closed any view of the map and file handles yet in this process so that when the other process tries to open the file. I thought error 1224 will be thrown.

Process 2:

HANDLE hFile = CreateFile("C:\\test\\full.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
    OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)
    cout << "Error Code : " << GetLastError() << endl;

char buffer[1025];
DWORD bytesRead;

if (!ReadFile(hFile, buffer, 1024, &bytesRead, 0))
    cout << "Error Code : " << GetLastError() << endl;

The CreateFile and ReadFile succeeds in the process 2.

Actually I am trying to handle this error 1224 separately. Which means when the file fails with error 32 I want to do something. and if the file fails with error 1224 I want to do something.

Now I must test those cases. In order to test those scenarios I should reproduce the error 1224.

Venkatesh
  • 1,537
  • 15
  • 28
  • Are you saying that the second process succeeds in opening the file? Well you do open it in *shared mode* in both processes. – Some programmer dude Jan 25 '17 at 06:30
  • @Someprogrammerdude Yeah If turn off shared mode then if failes with error 32, not with error 1224. I want to reproduce error 1224. – Venkatesh Jan 25 '17 at 06:32
  • Then you should to tell us *why* you want that specific error. And what the *actual* problem you try to solve is ([related reading about the XY problem](http://xyproblem.info/)). – Some programmer dude Jan 25 '17 at 06:34
  • @Someprogrammerdude added some more description. – Venkatesh Jan 25 '17 at 06:39
  • 1
    My (brief) research suggests that error 1224 occurs when you specify `CREATE_ALWAYS` in the call to CreateFile (in process #2) specifying that the file should be overwritten if it already exists. I guess it might also happen if you call DeleteFile() on a file with a memory mapping open, assuming the original handle was opened with `FILE_SHARE_DELETE`. – Harry Johnston Jan 25 '17 at 08:09
  • @HarryJohnston Thank you. It helped reproduce it. :) – Venkatesh Jan 25 '17 at 08:51
  • Excellent. When you have a chance, please post an answer, preferably including the "working" (i.e., failing in the desired way!) code. – Harry Johnston Jan 25 '17 at 08:54

2 Answers2

5

I want to really understand when the ERROR_USER_MAPPED_FILE occurs

if really understand let some extra info.

at first this error returned from kernel. so it was initially NTSTATUS error code, which than translated to win32 error by RtlNtStatusToDosError. api for reverse translation not exist - because it not uniquely, but possible yourself write code, which create reverse translation map. two NTSTATUS codes is translated to ERROR_USER_MAPPED_FILE : STATUS_USER_MAPPED_FILE and STATUS_PURGE_FAILED:

if (
    RtlNtStatusToDosError(STATUS_USER_MAPPED_FILE) != ERROR_USER_MAPPED_FILE
    ||
    RtlNtStatusToDosError(STATUS_PURGE_FAILED) != ERROR_USER_MAPPED_FILE 
    )
{
    __debugbreak();
}

for completely understand when this codes returned need look File System source code. we have fastfat example. if search for STATUS_USER_MAPPED_FILE - we can found that this code returned from 4 place:

and you can note common pattern here:

if (!MmCanFileBeTruncated())  return STATUS_USER_MAPPED_FILE;

about demo code for reproduce this error - for what you need two processes ?!? single routine in single process - more than enough.

void test()
{
    // for simplicity, i have access to this location as admin
    STATIC_OBJECT_ATTRIBUTES(oa, "\\systemroot\\temp\\.tmp");
    HANDLE hFile, hSection, hFile2;
    IO_STATUS_BLOCK iosb;
    LARGE_INTEGER AllocationSize = { PAGE_SIZE };
    NTSTATUS status;

    if (0 <= NtCreateFile(&hFile, FILE_GENERIC_WRITE|FILE_GENERIC_READ, &oa, &iosb, &AllocationSize, 0, 
        FILE_SHARE_VALID_FLAGS, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, 0, 0))
    {
        status = NtCreateSection(&hSection, SECTION_MAP_READ|SECTION_MAP_WRITE, 0, &AllocationSize, PAGE_READWRITE, SEC_COMMIT, hFile);

        if (0 <= status)
        {
            PVOID BaseAddress = 0;
            SIZE_T ViewSize = 0;
            status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY);
            NtClose(hSection);

            if (0 <= status)
            {
                LARGE_INTEGER Eof = {};

                // SetEndOfFile win32 api
                status = ZwSetInformationFile(hFile, &iosb, &Eof, sizeof(Eof), FileEndOfFileInformation);

                if (status != STATUS_USER_MAPPED_FILE) __debugbreak();

                // no win32 api
                status = ZwSetInformationFile(hFile, &iosb, &Eof, sizeof(Eof), FileAllocationInformation);

                if (status != STATUS_USER_MAPPED_FILE) __debugbreak();

                // SetFileValidData win32 api
                // we need have SE_MANAGE_VOLUME_NAME privilege, otherwise STATUS_PRIVILEGE_NOT_HELD
                status = ZwSetInformationFile(hFile, &iosb, &Eof, sizeof(Eof), FileValidDataLengthInformation);

                switch (status)
                {
                case STATUS_USER_MAPPED_FILE:
                case STATUS_PRIVILEGE_NOT_HELD:
                    break;
                default:  __debugbreak();
                }

                //CreateFileW(L"\\\\?\\c:\\windows\\temp\\.tmp", FILE_GENERIC_READ|FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0, CREATE_ALWAYS, 0, 0);

                status = NtCreateFile(&hFile2, FILE_GENERIC_WRITE|FILE_GENERIC_READ, &oa, &iosb, 0, 0, 
                    FILE_SHARE_VALID_FLAGS, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, 0, 0);

                if (status != STATUS_USER_MAPPED_FILE) __debugbreak();

                status = NtCreateFile(&hFile2, FILE_GENERIC_WRITE|FILE_GENERIC_READ, &oa, &iosb, 0, 0, 
                    FILE_SHARE_VALID_FLAGS, FILE_SUPERSEDE, FILE_SYNCHRONOUS_IO_NONALERT, 0, 0);

                if (status != STATUS_USER_MAPPED_FILE) __debugbreak();

                ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
            }
        }

        NtClose(hFile);
    }
}

about STATUS_PURGE_FAILED - look like in can be returned when we call WriteFile - look here - but i can not reproduce it in test. however very rarely this error can occur - "STATUS_PURGE_FAILED" error when you perform VM replications by using SCVMM in Windows Server 2012 R2

RbMm
  • 31,280
  • 3
  • 35
  • 56
2

ERROR_USER_MAPPED_FILE or error 1224 occurs when you try to delete or overwrite a file which has been mapped by other process.

Process #1

HANDLE hFile = CreateFile("C:\\test\\full.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
    OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
    return GetLastError();

HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hMapFile == INVALID_HANDLE_VALUE)
    return GetLastError();

mapHandles.push_back(hMapFile);

viewHandles.push_back(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0));

Process #2

HANDLE hFile = CreateFile("C:\\test\\full.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
    if(GetLastError() == 1224)
        // File has been mapped by other process
    else
        // some other issue
}

In Process #2, CREATE_ALWAYS tries to overwrite a file which has been mapped. So it ends up throwing error 1224.

Venkatesh
  • 1,537
  • 15
  • 28