Consider the following code fragment:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
int fd = open( "/path/to/existing/file/or/device", O_RDONLY);
int numberOfWords = 4096; // chosen to be smaller than file size
int* data = mmap( NULL, nomberOfWords * sizeof(int), PROT_READ, MAP_SHARED, fd, 0);
if (data != MAP_FAILED) {
printf( "%d\n", data[0]);
// oops, forgot to munmap
close(fd);
printf( "%d\n", data[0]); // <-- why doesn't this segfault
}
I am working with a custom kernel driver that uses ioctl()
to setup for DMA, and ultimately requires user space to use mmap()
to access a particular buffer.
While developing unit tests I discovered accidentally that after closing the file descriptor without calling munmap
first, it was still possible to access the buffer memory in user space with the mmap'ed pointer. Thinking there was some bug in the driver I wrote a small program similar to that shown here to exercise mmap() with a "normal" file.
What I was expecting to see is a segfault on the read after close, my thinking being, that the kernel would automatically munmap()
the pages associated with the file descriptor when the use of the open file descriptor was closed, similar to how it happens when the process is terminated.
Instead, I was able to keep using the pointer. This was a bit surprising as I have been using mmap()
for several years, I must have been smart (more likely lucky) enough to avoid bugs that would expose this situation. Nothing was obvious in the mmap
man page.
Ideally our driver will need to cause a segfault in user space if this happens, because we don't want a buggy user space program writing to the memory of interest.
So, is this behaviour the same across different *nix? In the given example, would it take say deleting the file to cause a segfault? Or perhaps flushing the vm caches?
Ok, after writing most of the question I found this different question which is worded differently from how I was searching: do I need to keep a file open after calling mmap on it?
The answer references the POSIX manual and it turns out in the man page after all (under munmap
, in passing :-| ) it is explained that closing the descriptor will not automatically unmap the mapping. So it looks like we need to modify our driver close code to invalidate associated memory mappings so that a segfault will occur in user space.
I decided to post the question in case someone else searches for a similar thing.
The man page for mmap states the following:
After the mmap() call has returned, the file descriptor, fd, can be closed immediately without invalidating the mapping.
This was added explicitly in August 2018, meaning in the man pages for the 5.x Linux kernels.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With