简短的回答是,您通常无法防范此类事情。如果文件可以在stat() 和mmap() 之间更改长度(可能是由于某些外部进程),为什么不能在mmap() 之后更改长度?换句话说(下面有详细的解释),你所要求的并不足以保护你免受对手的伤害。
如果你真的想检查 mmap 之后的映射是否仍然有效(即文件没有变短),你可以(ab)在 linux 上使用 remap_file_pages(未经测试);但是,由于上述原因,如果有人在不久之后将其截断,那将无济于事。
另请参阅手册页中的此警告:
未指定更改映射的基础文件大小对对应于文件添加或删除区域的页面的影响。
您需要某种形式的文件锁定来保护自己。正如您在 cmets 中所说,您正在与一个不尊重咨询锁定的对手打交道,除非您使用(罕见的)mandatory locking。
我认为这里完全安全的唯一方法是:
- 不要使用
mmap
- 更改文件的权限或复制文件,使攻击者无法访问它/副本。
为了进一步说明攻击者在你mmap之后改变文件长度的问题,考虑下面的测试程序。这将创建一个长度为 LONGFILE 的文件,打开它,然后:
- 模拟对手截断它,然后
mmap就是它;或
-
mmap 就是它,然后模拟对手截断它
在两个实例中,都会产生分段错误。因此,如您所说,如果您担心您的对手可能会在您打开文件后更改文件的长度,那么您根本不应该mmap'ing 它。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
*/
#undef TRUNCATE_BEFORE_MMAP
#define TRUNCATE_AFTER_MMAP
char *testfile = "/tmp/mmaptest";
#define SHORTFILE 10
#define LONGFILE 81920
int
main (int argc, char **argv)
{
int fd;
int sum;
size_t size = LONGFILE;
int i;
char *buf;
if ((fd = open (testfile, O_WRONLY | O_CREAT, 0777)) < 0)
{
perror ("initial open");
exit (1);
}
close (fd);
if (truncate (testfile, LONGFILE) < 0)
{
perror ("truncate");
exit (1);
}
if ((fd = open ("/etc/services", O_RDONLY)) < 0) /* a short file */
{
perror ("open");
exit (1);
}
#ifdef TRUNCATE_BEFORE_MMAP
if (truncate (testfile, SHORTFILE) < 0)
{
perror ("truncate");
exit (1);
}
#endif
if (MAP_FAILED == (buf = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0)))
{
perror ("mmap");
exit (1);
}
#ifdef TRUNCATE_AFTER_MMAP
if (truncate (testfile, SHORTFILE) < 0)
{
perror ("truncate");
exit (1);
}
#endif
for (i = 0; i < size; i++)
{
sum += buf[i];
}
if (munmap (buf, size) < 0)
{
perror ("munmap");
exit (1);
}
if (close (fd) < 0)
{
perror ("close");
exit (1);
}
exit (0);
}