mmap bug?

From: Giorgos Keramidas <keramida_at_freebsd.org>
Date: Sun, 31 Jul 2005 17:18:01 +0300
The following program can be used to mmap() a region outside of the
current size of a file and then write to it.  Should this ``expand''
the current file size?  If not, should writes on this area fail?

What's more interesting is that after running it on -CURRENT, I can
write data that is "attached" to the file but this doesn't affect the
current size of the file.

The test program I used was:

#   #include <sys/mman.h>
#   #include <sys/stat.h>
#
#   #include <err.h>
#   #include <errno.h>
#   #include <fcntl.h>
#   #include <stdio.h>
#   #include <stdlib.h>
#   #include <unistd.h>
#
#   static int      mmap_test(char *fname, size_t offset, size_t len);
#   static int      mmap_dump(unsigned char *, size_t);
#   static int      mmap_write_test(unsigned char *, size_t);
#   static void     usage(void);
#
#   int
#   main(int argc, char *argv[])
#   {
#           char *fname, *errp;
#           long val;
#           size_t offset, len;
#
#           if (argc != 4)
#                   usage();
#
#           fname = argv[1];
#
#           errno = 0;
#           errp = NULL;
#           val = strtol(argv[2], &errp, 0);
#           if ((errp != NULL && *errp != '\0') || errno != 0)
#                   err(1, "strtol: %s", argv[2]);
#           offset = (size_t)val;
#
#           errno = 0;
#           errp = NULL;
#           val = strtol(argv[3], &errp, 0);
#           if ((errp != NULL && *errp != '\0') || errno != 0)
#                   err(1, "strtol: %s", argv[3]);
#           len = (size_t)val;
#
#           if (mmap_test(fname, offset, len) != 0)
#                   exit(EXIT_FAILURE);
#
#           return (0);
#   }
#
#   static void
#   usage()
#   {
#           fprintf(stderr, "usage: foo file offset len\n");
#           exit(EXIT_FAILURE);
#   }
#
#   static int
#   mmap_test(char *fname, size_t offset, size_t len)
#   {
#           int fd;
#           void *ptr;
#
#           fd = open(fname, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
#           if (fd == -1)
#                   err(1, "open: %s", fname);
#
#           ptr = mmap(NULL, len, PROT_READ | PROT_WRITE,
#               MAP_SHARED, fd, offset);
#           if (ptr == NULL)
#                   err(1, "mmap");
#
#           mmap_dump((unsigned char *)ptr, len);
#           mmap_write_test((unsigned char *)ptr, len);
#           mmap_dump((unsigned char *)ptr, len);
#
#           if (munmap(ptr, len) != 0)
#                   err(1, "munmap");
#
#           close(fd);
#           return (0);
#   }
#
#   static int
#   mmap_dump(unsigned char *p, size_t len)
#   {
#           size_t k;
#
#           for (k = 0; k < len; k++) {
#                   if ((k % 16) == 0)
#                           printf("%08lx: ", (unsigned long)k);
#                   printf(" %02x", p[k]);
#                   if ((k % 16) == 15)
#                           printf("\n");
#           }
#           if ((k % 16) != 15)
#                   printf("\n");
#           return (0);
#   }
#
#   static int
#   mmap_write_test(unsigned char *p, size_t len)
#   {
#           size_t k;
#
#           for (k = 0; k < len; k++)
#                   p[k] = (unsigned char)(k % 256);
#           return (0);
#   }

Here's a sample run:

# gothmog:/tmp/foo$ dd if=/dev/zero of=tempfile bs=512 count=10
# 10+0 records in
# 10+0 records out
# 5120 bytes transferred in 0.000575 secs (8903332 bytes/sec)
# gothmog:/tmp/foo$ ls -l tempfile
# -rw-rw-r--  1 giorgos  wheel  - 5120 Jul 31 17:12 tempfile

ok, the file has been created with the right size.

# gothmog:/tmp/foo$ ./foo tempfile 5120 60
# 00000000:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# 00000010:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# 00000020:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# 00000030:  00 00 00 00 00 00 00 00 00 00 00 00
# 00000000:  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
# 00000010:  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
# 00000020:  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
# 00000030:  30 31 32 33 34 35 36 37 38 39 3a 3b

mmap() succeeded and wrote after the previous end of the file.

# gothmog:/tmp/foo$ ls -l tempfile
# -rw-rw-r--  1 giorgos  wheel  - 5120 Jul 31 17:12 tempfile
# gothmog:/tmp/foo$ hd tempfile
# 00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
# *
# 00001400

The size of the file is still the same!

# gothmog:/tmp/foo$ ./foo tempfile 5120 60
# 00000000:  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
# 00000010:  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
# 00000020:  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
# 00000030:  30 31 32 33 34 35 36 37 38 39 3a 3b
# 00000000:  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
# 00000010:  10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
# 00000020:  20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
# 00000030:  30 31 32 33 34 35 36 37 38 39 3a 3b

Somehow, the data written by the first mmap() is still there, but is not
visible as part of the file size or by using ``normal'' commands that
access its contents.
Received on Sun Jul 31 2005 - 12:20:20 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:40 UTC