* System writes out modified portions of the last memory mapped page beyond file end if file is resized
@ 2026-10-09 14:09 Vyacheslav Kovalevsky
2026-10-09 14:47 ` Ryusuke Konishi
0 siblings, 1 reply; 2+ messages in thread
From: Vyacheslav Kovalevsky @ 2026-10-09 14:09 UTC (permalink / raw)
To: konishi.ryusuke, slava; +Cc: linux-nilfs, linux-kernel
Detailed description
====================
Hello, there seems to be an issue with NILFS2:
1. Create a file, truncate it to some size that is not multiple of OS
page size (which is usually 4096).
2. Open the file.
3. Create a new shared mapping (MAP_SHARED) using `mmap` from 0 to some
point beyond file end (but still on the same OS page as the file end,
otherwise next steps will fail).
4. Write some data beyond file end using the mapping.
5. Resize file (e.g. using `truncate`) so the file end is now after the
mapping range.
6. Observe that bytes written beyond file end (using the mapping) were
also written to backing file after resizing.
The last bit violates the POSIX specification, extract from `mmap`
documentation at
<https://pubs.opengroup.org/onlinepubs/9799919799/functions/mmap.html>:
> The system shall always zero-fill any partial page at the end of an
object. Further, __the system shall never write out any modified
portions of the last page of an object which are beyond its end__.
Linux manual <https://man7.org/linux/man-pages/man2/mmap.2.html>:
> For a file that is not a multiple of the page size, the remaining
bytes in the partial page at the end of the mapping are zeroed when
mapped, and __modifications to that region are not written out to the
file__.
For reference: `ext4` / `btrfs` / `xfs` do not write the data to file
after resizing, as
The exact same issue was recently reported for ZFS:
<https://github.com/openzfs/zfs/issues/19220>
System info
===========
Linux version 7.0.0-34-generic (Ubuntu 26.04.1 LTS)
nilfs-tools version 2.2.11
How to reproduce
================
```
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define WRITE_OFF 10
#define WRITE_SIZE 10
#define WRITE_DATA "Test data!"
#define MMAP_SIZE WRITE_SIZE + WRITE_OFF
#define FILE_SIZE_INI 10
#define FILE_SIZE_END 32
int main() {
char buffer[FILE_SIZE_END + 1] = {};
int status;
int fd;
void *addr;
status = creat("file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
printf("CREAT: %d\n", status);
close(status);
status = truncate("file", FILE_SIZE_INI);
printf("TRUNCATE: %d\n", status);
status = open("file", O_RDWR);
printf("OPEN: %d\n", status);
fd = status;
addr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
printf("MMAP: FAILED\n");
return EXIT_FAILURE;
}
printf("MMAP: OK\n");
close(fd);
memcpy(((char *)addr) + WRITE_OFF, WRITE_DATA, WRITE_SIZE);
status = munmap(addr, MMAP_SIZE);
printf("MUNMAP: %d\n", status);
status = truncate("file", FILE_SIZE_END);
printf("TRUNCATE: %d\n", status);
status = open("file", O_RDONLY);
printf("OPEN: %d\n", status);
fd = status;
status = read(fd, buffer, FILE_SIZE_END);
printf("READ: %d\n", status);
printf("BYTES: ");
for (int i = 0; i < status; i++) {
printf("%02x ", buffer[i]);
}
printf("\n");
}
// ::Expected::
// CREAT: 3
// TRUNCATE: 0
// OPEN: 3
// MMAP: OK
// MUNMAP: 0
// TRUNCATE: 0
// OPEN: 3
// READ: 32
// BYTES: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00
// ::Actual::
// CREAT: 3
// TRUNCATE: 0
// OPEN: 3
// MMAP: OK
// MUNMAP: 0
// TRUNCATE: 0
// OPEN: 3
// READ: 32
// BYTES: 00 00 00 00 00 00 00 00 00 00 54 65 73 74 20 64 61 74 61 21 00
00 00 00 00 00 00 00 00 00 00 00
```
Steps:
1. Create and mount new NILFS2 filesystem in default configuration.
2. Change directory to root of the filesystem and run the compiled test.
3. Observe that last operation returns unexpected value.
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: System writes out modified portions of the last memory mapped page beyond file end if file is resized
2026-10-09 14:09 System writes out modified portions of the last memory mapped page beyond file end if file is resized Vyacheslav Kovalevsky
@ 2026-10-09 14:47 ` Ryusuke Konishi
0 siblings, 0 replies; 2+ messages in thread
From: Ryusuke Konishi @ 2026-10-09 14:47 UTC (permalink / raw)
To: Vyacheslav Kovalevsky; +Cc: slava, linux-nilfs, linux-kernel
On Fri, Oct 9, 2026 at 11:09 PM Vyacheslav Kovalevsky wrote:
>
> Detailed description
> ====================
>
> Hello, there seems to be an issue with NILFS2:
>
> 1. Create a file, truncate it to some size that is not multiple of OS
> page size (which is usually 4096).
> 2. Open the file.
> 3. Create a new shared mapping (MAP_SHARED) using `mmap` from 0 to some
> point beyond file end (but still on the same OS page as the file end,
> otherwise next steps will fail).
> 4. Write some data beyond file end using the mapping.
> 5. Resize file (e.g. using `truncate`) so the file end is now after the
> mapping range.
> 6. Observe that bytes written beyond file end (using the mapping) were
> also written to backing file after resizing.
>
> The last bit violates the POSIX specification, extract from `mmap`
> documentation at
> <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mmap.html>:
>
> > The system shall always zero-fill any partial page at the end of an
> object. Further, __the system shall never write out any modified
> portions of the last page of an object which are beyond its end__.
>
> Linux manual <https://man7.org/linux/man-pages/man2/mmap.2.html>:
>
> > For a file that is not a multiple of the page size, the remaining
> bytes in the partial page at the end of the mapping are zeroed when
> mapped, and __modifications to that region are not written out to the
> file__.
>
> For reference: `ext4` / `btrfs` / `xfs` do not write the data to file
> after resizing, as
>
> The exact same issue was recently reported for ZFS:
> <https://github.com/openzfs/zfs/issues/19220>
>
>
> System info
> ===========
>
> Linux version 7.0.0-34-generic (Ubuntu 26.04.1 LTS)
> nilfs-tools version 2.2.11
>
>
> How to reproduce
> ================
>
> ```
> #include <err.h>
> #include <fcntl.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/mman.h>
> #include <sys/stat.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> #define WRITE_OFF 10
> #define WRITE_SIZE 10
> #define WRITE_DATA "Test data!"
> #define MMAP_SIZE WRITE_SIZE + WRITE_OFF
> #define FILE_SIZE_INI 10
> #define FILE_SIZE_END 32
>
> int main() {
> char buffer[FILE_SIZE_END + 1] = {};
> int status;
> int fd;
> void *addr;
>
> status = creat("file", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
> printf("CREAT: %d\n", status);
> close(status);
>
> status = truncate("file", FILE_SIZE_INI);
> printf("TRUNCATE: %d\n", status);
>
> status = open("file", O_RDWR);
> printf("OPEN: %d\n", status);
> fd = status;
>
> addr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> if (addr == MAP_FAILED) {
> printf("MMAP: FAILED\n");
> return EXIT_FAILURE;
> }
> printf("MMAP: OK\n");
> close(fd);
>
> memcpy(((char *)addr) + WRITE_OFF, WRITE_DATA, WRITE_SIZE);
>
> status = munmap(addr, MMAP_SIZE);
> printf("MUNMAP: %d\n", status);
>
> status = truncate("file", FILE_SIZE_END);
> printf("TRUNCATE: %d\n", status);
>
> status = open("file", O_RDONLY);
> printf("OPEN: %d\n", status);
> fd = status;
>
> status = read(fd, buffer, FILE_SIZE_END);
> printf("READ: %d\n", status);
> printf("BYTES: ");
> for (int i = 0; i < status; i++) {
> printf("%02x ", buffer[i]);
> }
> printf("\n");
> }
>
> // ::Expected::
> // CREAT: 3
> // TRUNCATE: 0
> // OPEN: 3
> // MMAP: OK
> // MUNMAP: 0
> // TRUNCATE: 0
> // OPEN: 3
> // READ: 32
> // BYTES: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> 00 00 00 00 00 00 00 00 00 00 00
>
> // ::Actual::
> // CREAT: 3
> // TRUNCATE: 0
> // OPEN: 3
> // MMAP: OK
> // MUNMAP: 0
> // TRUNCATE: 0
> // OPEN: 3
> // READ: 32
> // BYTES: 00 00 00 00 00 00 00 00 00 00 54 65 73 74 20 64 61 74 61 21 00
> 00 00 00 00 00 00 00 00 00 00 00
> ```
>
> Steps:
>
> 1. Create and mount new NILFS2 filesystem in default configuration.
> 2. Change directory to root of the filesystem and run the compiled test.
> 3. Observe that last operation returns unexpected value.
Hi Vyacheslav Kovalevsky,
Thank you for your report. I have registered it as issue #179:
https://github.com/nilfs-dev/nilfs2/issues/179
Having the reproduction steps clearly outlined is extremely helpful.
This appears to be an unaddressed bug, but I'd like to begin the
investigation by verifying whether the issue can be reproduced with
the latest code.
Thanks,
Ryusuke Konishi
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-09 14:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-09 14:09 System writes out modified portions of the last memory mapped page beyond file end if file is resized Vyacheslav Kovalevsky
2026-10-09 14:47 ` Ryusuke Konishi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®