Why is Windows's CreateFile(<no share access>) lying to me? - c

I'm trying to prevent a third-party DLL in my process from reading a file I've opened, and I've found it to be... well, impossible.
No matter what I do, no matter what share flags I specify, their call always seems to succeed!
Here is the screenshot from Process Monitor -- the first CreateFile call is mine, and the rest are theirs:
How is this even possible? Why is the "Share Mode: None" lying to me, and how can I prevent this?
This code below is an example that reproduces the problem:
#include <stdio.h>
#include <Windows.h>
int main()
{
LPCTSTR file = TEXT("C:\\Test1234.xml");
HANDLE hFile1 =
CreateFile(file, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_ALWAYS, 0, NULL);
HANDLE hFile2 =
CreateFile(file, FILE_READ_DATA, 0, NULL, OPEN_ALWAYS, 0, NULL);
DWORD n;
BYTE buf[1];
printf("%d\n", ReadFile(hFile2, buf, sizeof(buf), &n, NULL));
CloseHandle(hFile1);
CloseHandle(hFile2);
DeleteFile(file);
}

Share modes are enforced for actually reading and writing the DATA of the file. Attributes (like file size, timestamps, etc) are not covered by the sharing rules and there is no way to prevent their access short of ACLs.
The best you can to is open the file for R/W/D access and not specify SHARE_READ|WRITE|DELETE.
Weird, but true.

Related

How to find the end of a memory mapped file in Windows platform without previously knowing the size of the file?

I have mapped a file of unknown size (around 4-6 GiB) in Windows platform and got a pointer to the start of the file data returned from the MapFileView function. But how can I know that I have reached the end of the file when I access the data using the pointer sequentially?
Here is the code I have so far written and it successfully maps the file and returns the pointer:
#include <Windows.h>
#include <stdio.h>
#include <inttypes.h>
int main()
{
HANDLE hFile = CreateFile("Test.bin",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!hFile)
{
printf("Could not create file (%lu).\n", GetLastError());
exit(1) ;
}
HANDLE hMapFile = CreateFileMappingA(hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL);
if (!hMapFile)
{
printf("Could not create file mapping object (%lu).\n", GetLastError());
CloseHandle(hFile);
exit(1);
}
int32_t* pBuf = (int32_t*) MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (!pBuf)
{
printf("Could not map file (%lu).\n", GetLastError());
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(1);
};
UnmapViewOfFile(pBuf);
CloseHandle(hFile);
CloseHandle(hMapFile);
exit(0);
}
So I wanted to read equal sized different parts of the file simultaneously in multiple threads. I believe mapped file is the right choice for this purpose. Advice about any other faster and possible approaches is highly appreciated.
I have researched some similar questions in the forum and I suppose this is the closest topic I could find:
Read all contents of memory mapped file or Memory Mapped View Accessor without knowing the size of it
But this answer is using C# and is not written using the WinAPI, therefore, I couldn't understand their process.
Thanks in advance :)
Call GetFileSizeEx to get the size of a file, and use this in combination with the base address and the current read address to determine where the end address is.

Is it legal to read a file descriptor into NULL?

Recently, I've been fixing the timestep for the sake of a library that I am writing. Following some research, suppose that I ended up with this prototype, precise and easy to combine with the generic event system of my library:
#include <stdio.h>
#include <unistd.h>
#include <sys/timerfd.h>
#include <poll.h>
struct pollfd fds[1];
struct itimerspec its;
int main(void) {
fds[0] = (struct pollfd) {timerfd_create(CLOCK_MONOTONIC, 0), POLLIN, 0}; //long live clarity
its.it_interval = (struct timespec) {0, 16666667};
its.it_value = (struct timespec) {0, 16666667};
timerfd_settime(fds[0].fd, 0, &its, NULL);
while(1) {
poll(fds, 1, -1);
if(fds[0].revents == POLLIN) {
long long buffer;
read(fds[0].fd, &buffer, 8);
printf("ROFL\n");
} else {
printf("BOOM\n");
break;
}
}
close(fds[0].fd);
return 0;
}
However, it severely hurt me that I've had to pollute my CPU caches with a whole precious 8 bytes of data in order to make the timer's file descriptor reusable. Because of that, I've tried to replace the read() call with lseek(), as follows:
lseek(fds[0].fd, 0, SEEK_END);
Unfortunately, both that and even lseek(fds[0].fd, 8, SEEK_CUR); gave me ESPIPE errors and would not work. But then, I found out that the following actually did its job, despite of giving EFAULTs:
read(fds[0].fd, NULL, 8);
Is it legal, defined behavior to offset the file descriptor like this? If it is not (as the EFAULTs suggested to me, strongly enough to refrain from using that piece of genius), does there exist a function that would discard the read data, without ever writing it down, or otherwise offset my timer's file descriptor?
The POSIX specification of read(2) does not specify the consequences of passing a null pointer as the buffer argument. No specific error code is given, nor does it say whether any data will be read from the descriptor.
The Linux man page has this error, though:
EFAULT buf is outside your accessible address space.
It doesn't say that it will read the 8 bytes and discard them when this happens, though.
So I don't think you can depend on this working as you desire.

Why does MapViewOfFile fail with ERROR_ACCESS_DENIED?

I came across this situation with WinAPI's MapViewOfFile function. An Internet search didn't turn up any apparent fixes, so I will share my problem and solution here.
Consider the following snippet:
const char *name = "Global\\Object_Name";
unsigned long size = get_object_size();
HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
size,
name);
if (!handle || handle == INVALID_HANDLE_VALUE)
exit(GetLastError());
bool created = GetLastError() == 0;
void *block = MapViewOfFile( handle,
FILE_MAP_ALL_ACCESS,
0,
0,
size);
if (block == NULL)
exit(GetLastError());
In one particular case, CreateFileMapping was successfully returning a handle. GetLastError was returning ERROR_ALREADY_EXISTS, so created == false. Now, the call to MapViewOfFile, using the same size that I passed to CreateFileMapping, returns NULL and GetLastError returns 0x05: ERROR_ACCESS_DENIED. The process was running with administrator privileges.
The MSDN documentation doesn't really mention any reason why this situation would occur. So why does CreateFileMapping succeed, but MapViewOfFile fail?
After a lot of suffer, I finally found what was causing this error in my application, in case someone else is struggling with the same, the problem is not with the MapViewOfFile method, but with the CreateFileMapping, the size of the createFileMapping should be the size of the file, not the size of the element to read, if you don't know the size then it should be 0, this does not apply to the MapViewOfFile as the value to pass as size is the length of the block you want to read/write.
Your code working will look like this:
const char *name = "Global\\Object_Name";
unsigned long size = get_object_size();
HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
0,
name);
if (!handle || handle == INVALID_HANDLE_VALUE)
exit(GetLastError());
bool created = GetLastError() == 0;
void *block = MapViewOfFile( handle,
FILE_MAP_ALL_ACCESS,
0,
0,
size);
if (block == NULL)
exit(GetLastError());
A just putting this here to document what I found, unfortunately is hard to search for this error when you don't know what is causing it. I hope this saves a couple of hours to someone else.
I'm sure there are many reasons why ERROR_ACCESS_DENIED could occur from a call to MapViewOfFile. In my particular situation, it was due to the size argument.
The hint is in the fact that created == false. It shows that the object "Global\\Object_Name" has already been created. For whatever reason, the creating call initialised the section with a smaller size. For what seems like an oversight, the second call to CreateFileMapping will happily give you a handle to the already-existing object, even if you asked for a bigger mapping.
The call to MapViewOfFile now fails, because it's requesting a view that is bigger than the actual section.
So, if you're in a similar situation where the second call to MapViewOfFile fails, check the size that you're trying to map to.
It could be that the second project is compiling with a different structure alignment, resulting in the sizeof() operator determining different values, or some other size-determining function is not behaving as expected.

My first windows named pipe, not sure what is wrong

Edit: Here is the entire code, ignore Romanian comments. Also 2 or 3 names are untranslated from Romanian: http://pastebin.com/JjtayvXX
I am trying to learn the basics of OS, now I'm working with named pipes under windows and I can't tell what's wrong.
Honestly I'm working off an example a friend did, but he's just as bad as me if not worse. While hi's program works (albeit it does something else), he can't explain anything, most likely just copied from somewhere, still ... not important, what I was trying to say I'm learning from examples, and not professional ones.
Server receives a message from the client, returns max and min numbers.
Server.c:
#include "windows.h"
#include "stdio.h"
struct Msg {
int numbers[20];
int length;
};
...
int main () {
HANDLE inputPipe, outputPipe;
Msg msg;
while (true) {
inputPipe = CreateNamedPipe ("\\\\.\\pipe\\Client2Server",
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0, //Numb of output bytes
sizeof(Msg), // Numb of input bytes
0, // Wait forever
NULL); // Don't know how to use security
ConnectNamedPipe (inputPipe,NULL);
// Here is where the server dies
ReadFile (inputPipe, &msg,sizeof(Msg),NULL,NULL);
Now Client.c:
struct Msg {
int numbers[20];
int length;
};
int main () {
HANDLE outputPipe, inputPipe;
Msg msg;
// #misc: read data from keyboard, create msg
outputPipe = CreateFile ("\\\\.\\pipe\\Client2Server",
GENERIC_WRITE,
FILE_SHARE_READ, // * comment after code
NULL, // again, I know nothing about security attributes
CREATE_ALWAYS, // either create or overwrite
0,
NULL);
// Here is where it dies
WriteFile (outputPipe, &msg, sizeof(Msg), NULL, NULL);
I get Access violation writing location 0x00000000. No idea why.
I would like that this process only writes, and another process (server) only reads. Is FILE_SHARE_READ OK ?
Also I don't know how to mess with CreationDisposition / FlagsAndAttributes (last 2 parameters at CreateFile), are they OK ?
Edit: Added actual answer, reference to other topic, tried it myself
WriteFile()'s fourth parameter (pointer to variable that will store number of bytes) should not be null. Based on the API description, this parameter can ONLY be NULL if the fifth param, lpOverlapped, is NOT null.
See similar topic here:
Why does WriteFile crash when writing to the standard output?
Can you check/printf the return values of ReadFile() (failed if return = 0 or FALSE) and client.c CreateFile() (failed if returns INVALID_HANDLE_VALUE) to see if they succeed?
If failed, can you print the value returned by GetLastError() immediately after the call so that we can see the specific error?

Calling VirtualProtect on a mapped file

I'm using the CreateFileMapping and MapViewOfFile functions to map a file into memory. After a certain point, I call VirtualProtect to change its protection from read-only to read and write. This call fails and GetLastError gives ERROR_INVALID_PARAMETER.
Here is a simplified version of my code that demonstrates the problem.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main() {
HANDLE fd, md;
char *addr;
DWORD old;
BOOL ok;
fd = CreateFile("filename", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
md = CreateFileMapping(fd, NULL, PAGE_READWRITE, 0, 100, NULL);
addr = MapViewOfFile(md, FILE_MAP_READ, 0, 0, 100);
ok = VirtualProtect(addr, 100, PAGE_READWRITE, &old);
if (!ok) {
// we fall into this if block
DWORD err = GetLastError();
// this outputs "error protecting: 87"
printf("error protecting: %u\n", err);
return 1;
}
UnmapViewOfFile(addr);
CloseHandle(md);
CloseHandle(fd);
return 0;
}
What am I doing wrong here? Am I not allowed to call VirtualProtect on a region containing a mapped file?
Start out by creating the view with FILE_MAP_READ | FILE_MAP_WRITE and protect with PAGE_READONLY. Now you have no trouble making it PAGE_READWRITE later:
addr = MapViewOfFile(md, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 100);
ok = VirtualProtect(addr, 100, PAGE_READONLY, &old);
//...
ok = VirtualProtect(addr, 100, PAGE_READWRITE, &old);
What happens in your code is that VirtualProtectEx (invoked by VirtualProtect of yours) fails with error STATUS_SECTION_PROTECTION (0xC000004E) - "A view to a section specifies a protection that is incompatible with the protection of the initial view" and this seems to be what you did indeed by creating a section view with more restrictive protection (FILE_MAP_READ).
This topic doesn't seem to be documented with enough details, so I think you'd better simply follow what Hans suggested.
According to http://msdn.microsoft.com/en-us/library/aa366556(v=vs.85).aspx this should be legal. According to VirtualProtect documentation, the new flags must be compatible with the "VirtualAlloc" flags - if this transfer to the "MapViewOfFile" flags, I'd suspect that you can tighten but not loosen the protection. Try mapping readwrite and changing protection to readonly.

Resources