Re: mmap() + write() bug

Mark Hemment (markhe@nextd.demon.co.uk)
Fri, 26 Sep 1997 12:07:37 +0100 (BST)


Hi Jim/All,

What you are seeing is a results of writing ahead of memory you are
reading.
mmap(MAP_SHARE) mappings, causes the kernel to share the pages in the
page cache with the address-space of the user task (obviously).
A write(2) sys-call drops down into the file-system specific code -
ext2_file_write() in fs/ext2/file.c [if your using ext2].
ext2_file_write() has, at its heart, a loop which basically does;
do {
Find (and if necessary load) the 'kernel' buffer for the
current offset in this file - call this buffer "b1"
Write to this 'kernel' buffer [b1] by copying from the
user buffer supplied to write(2)
Call update_vm_cache(b1) to ensure the page-cache is kept
in-sync with the buffers/on-disk-data
Mark the buffer as dirty (so that it is written out)
Update the file offset
} while (more to go);

NOTE: Only one buffer's worth is copied from user-space [into a
'kernel' buffer] for each loop, and this copied data is passed to
update_vm_cache().

update_vm_cache() checks if the require page is loaded into the
page-cache. If it is loaded, then it is updated (with a memcpy()) from
the passed buffer.

The user-buffer is the same page(s) as the page(s) in the page-cache.
Unless your system is under load, this page(s) will be loaded due to the
memset() you performed in your code. Therefore, the memcpy() in
update_vm_cache() is actually modifying the user buffer!

When write(2)ing a small buffer, that does not cross any 'kernel' buffer
boundaries, then everything works as you expect - the loop in
ext2_file_write() is only executed once.
When the loop is executed more than once, the modified data is read from
the user-buffer - causing the effect you are seeing.

Hope I've explained this well enough....

Regards,

markhe

------------------------------------------------------------------
Mark Hemment, Unix/C Software Engineer (Contractor)
markhe@nextd.demon.co.uk http://www.nextd.demon.co.uk/
"Success has many fathers, failure is a B**TARD!" - anon
------------------------------------------------------------------

On Thu, 25 Sep 1997, Jim Nance wrote:
> Hello David,
> I think I have found a bug or at least a wart in the kernel. I was
> playing around with some ideas for how to efficiently delete the center
> of a file by mmap()ing it into the address space, and then calling
> write() back to the same file using the mmap()ed area as the input to
> write(). This seems to work quite well, as does a similar process using
> read(). I was actually quite impressed that this worked, so I decided to
> see how it would work if I tried to duplicate a block in the center of a
> file using a similar process. That did not work as well. For example,
> lets say I have a file with 5 chars in it: "abcde" If I want to duplicate
> the c character, I can do:
>
> fd = open(file, O_RDWR);
> ptr = mmap(0, stuff, fd, 0);
> lseek(fd, 3, SEEK_SET);
> write(fd, ptr+2, 3);
>
> and this should produce a file which contains "abccde", and it does for
> this case. If instead of a single character, each letter represents a
> large block of characters, then I get a file that looks like "abcccc".