Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WinAPI: Is it needed to call FlushInstructionCache on an executable memory-mapped file?

I have written a short program to read a windows obj file and find the .text section and run the code in it. To do this I make the following Windows API function calls (Full code [gist.github.com], for those interested):

HANDLE FileHandle = CreateFile("lib.obj",
                               GENERIC_READ | GENERIC_EXECUTE,
                               FILE_SHARE_READ, 0,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

HANDLE MappingHandle = CreateFileMapping(FileHandle, 0, PAGE_EXECUTE_READ, 0, 0, 0);

void *Address = MapViewOfFile(MappingHandle, FILE_MAP_EXECUTE | FILE_MAP_READ,
                              0, 0, 0);

I then find the .text section in the file and cast the pointer to the code to a function pointer in C++ and simply call the function. This actually appeared to work for me.

Have I made a mistake not calling FlushInstructonCache on the range of virtual memory mapped to the file?

I ask this because I was recently reading the VirtualAlloc documentation and it notes at the bottom:

When creating a region that will be executable, the calling program bears responsibility for ensuring cache coherency via an appropriate call to FlushInstructionCache once the code has been set in place. Otherwise attempts to execute code out of the newly executable region may produce unpredictable results.

Is it possible that my code will cause the CPU to execute old instructions in the instruction cache?

There is no such note on the MapViewOfFile or CreateFileMapping pages.

like image 761
Fsmv Avatar asked Feb 27 '17 06:02

Fsmv


1 Answers

If you only load the file-content into memory using MapViewOfFile, it should be fine without.

If you MODIFY the content in memory, you need to flush the instructioncache before executing the code, as it MAY exist in cache in the unmodified form, and MAY then be executed without your modifications.

I use the word MAY because of two things:

  1. it depends on the processor architecture whether the processor detects writes to the memory it is about to execute [some processors don't even have hardware to register writes to data that is in instruction caches - because it's so rare that it's very unlikely].

  2. because it's hard to predict what may be in a cache - processors have all manner of "clever" ways to prefetch and in general "fill" caches.

Obviously, VirtualAlloc has zero chance of containing the data you wanted, so it's mentioned there because you'd ALWAYS write to it before executing.

Modifications include "fix up for absolute addresses" for example (something you'd have to do if you want to complete a project that loads something complex to execute it), or if you write a debugger, when you set a breakpoint by replacing an instruction with the INT 3 instruction on x86.

A second case of "modification" is if you unload the file, and load a different file (perhaps the "same" file, but rebuilt, for example), in which case, the previously executed code may still be in the cache, and you get the mysterious "why didn't my changes do what I expect"

like image 129
Mats Petersson Avatar answered Oct 19 '22 04:10

Mats Petersson