5

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

}

Background

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.

Problem

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?

6EQUJ5
  • 3,142
  • 1
  • 21
  • 29

2 Answers2

11

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.

Community
  • 1
  • 1
6EQUJ5
  • 3,142
  • 1
  • 21
  • 29
  • 2
    It is _wrong_ for your driver to invalidate a memory mapping just because the file has been closed. The documented behavior of `mmap` is that you can _keep using_ a mmap-ed region after the file has been closed. Your driver should get a notification from the core kernel when mappings actually go away, as well as when each file descriptor is closed. – zwol Jul 20 '16 at 14:31
1

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.

Cimbali
  • 11,012
  • 1
  • 39
  • 68