如何处理stat和后续mmap之间的文件大小变化?

时间:2015-02-16 19:35:47

标签: c linux file posix mmap

要确定mmap调用的大小,我使用stat,并将获取的大小作为要创建的映射的相应长度传递。如果文件大小在调用之间发生变化,我的理解是它将只映射文件的一部分,或者在收缩的情况下,我将不具有要引用的映射的实际大小并在访问时获取SIGBUS不属于基础对象的范围。

如何干净地处理这个案子?

2 个答案:

答案 0 :(得分:2)

简短的回答是,你无法防范这种事情。如果文件可能在stat()mmap()之间改变了长度(可能是由于某些外部过程),为什么它不能在mmap()之后改变长度?换句话说(下面的详细解释),你所要求的并不是足够来保护你免受对手的伤害。

如果你真的想检查映射在mmap后仍然有效(即文件没有缩短),你可以(ab)在linux上使用remap_file_pages(未经测试);但是,由于出现的原因,如果有人在不久之后将其截断,将无法帮助您。

另请参阅联机帮助页中的此警告:

  

未指定在与文件的添加或删除区域对应的页面上更改映射的基础文件大小的效果。

您需要某种形式的文件锁定才能保护自己。正如你在评论中所说,你正在与一个不尊重顾问锁定的对手打交道,那么除非你使用(罕见)mandatory locking

我相信在这里完全安全的唯一方法是:

  • 请勿使用mmap
  • 更改文件的权限或复制文件,以便攻击者无法访问该文件/副本。

为了进一步说明攻击者在mmap之后更改文件长度的问题,请考虑下面的测试程序。这将创建一个长度为LONGFILE的文件,打开它,然后:

  • 模拟对手截断它,然后mmap就是它;或
  • mmap是它,然后模拟一个截断它的对手

两个实例中,都会创建分段错误。因此,正如您所说,如果您担心对手可能会在打开文件后更改文件的长度,那么您根本就不应该mmap这样做。

#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);
}

答案 1 :(得分:0)

对于长度不一的东西,

mmap不是很容易使用。如果您控制进程的所有方面,则可以使用信号量在读取器和写入器之间进行并发控制,包括让编写器将正确的长度写入头字段。如果要映射某个任意进程可以增长或缩小的文件,您可能需要使用信号处理程序来保护自己,更不用说对内容进行任意更改。