write system call violates POSIX standard

Bruce Evans brde at optusnet.com.au
Thu Feb 16 16:32:47 UTC 2012

On Thu, 16 Feb 2012, Bruce Evans wrote:

> ...
> Here is a corresponding test to show the complete brokenness of
> RLIMIT_FSIZE for [f]truncate():

I tried this under Linux-2.6.10.  Linux worked like I think is correct
for truncate, but not for write.

> %%%
> #include <sys/resource.h>
> #include <sys/stat.h>
> #include <err.h>
> #include <fcntl.h>
> #include <signal.h>
> #include <stdint.h>
> #include <stdio.h>
> #define LIMSIZE		60000
> int
> main(void)
> {
> 	struct rlimit lim;
> 	struct stat sb;
> 	int fd;
> 	if (signal(SIGXFSZ, SIG_IGN) == SIG_ERR)
> 		err(1, "signal");
> 	if (getrlimit(RLIMIT_FSIZE, &lim) != 0)
> 		err(1, "getrlimit");
> 	lim.rlim_cur = LIMSIZE;
> 	if (setrlimit(RLIMIT_FSIZE, &lim) != 0)
> 		err(1, "setrlimit");
> 	fd = open("result.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
> 	if (fd < 0)
> 		err(1, "open");
> 	if (fstat(fd, &sb) != 0)
> 		err(1, "first stat");
> 	if (sb.st_size != 0)
> 		errx(1, "O_TRUNC failed to truncate the file");
> 	if (ftruncate(fd, 2 * LIMSIZE) != 0)
> 		err(1, "ftruncate");

I had to fix this.  With a working truncate, this is expected to fail.
Linux failed correctly.

> 	if (fstat(fd, &sb) != 0)
> 		err(1, "stat");
> 	warnx("size = %jd", (intmax_t)sb.st_size);
> 	if (sb.st_size == 2 * LIMSIZE)
> 		errx(1, "ftruncate failed to honour RLIMIT_FSIZE, as expected");
> 	if (sb.st_size != 0)
> 		errx(1, "ftruncate worked incorrectly, but not as expected");
> 	errx(0, "ftruncate worked correctly, but not as expected");
> }
> %%%

> ...
> POSIX has fuzzy wording for the interaction of these bugs.  Suppose
> that the file size is already larger than the rlimit, and we try to
> truncate it to its current size.  Is this a null change or an EFBIG
> error?  POSIX only says (for [f]truncate) that "if the request _would_
> _cause_ the file size to exceed the soft file limit, [then it is an
> error]".  I think a null change "wouldn't cause" the file to exceed
> the limit in this case, because the cause of exceeding the limit is
> that the limit was already exceeded.  However, it takes a delicate
> reading of "would case" to get this interpretation, and FreeBSD never
> did it this way in cases where it actually checks the limit -- for
> write(), the limit is checked before even looking at the current
> file size.  The centralization of the limit checking makes it harder
> to change this, because the central function doesn't know the file
> size.
> Truncations that would reduce the file size from beyond the limit to less
> beyond the limit are also interesting.  Are these allowed?  Now they
> cause something, but they don't cause the file size to exceed the limit,
> so a strict reading of "would cause" again allows them.

Linux allows such truncations.  To test this, remove the O_TRUNC from
the above and copy a file larger than 120000 bytes to result.txt before
running the program.

> write() has some very nice, different bugs depending on the
> interpretation of to the corresponding "would cause" for it.  In
> FreeBSD, because the limit checking is done before even looking at the
> size of the file, write()s to the middle of a big file are rejected
> if they would extend past the limit.  But the POSIX specification is
> that "if the request _would_ _cause_ the file size to exceed the soft
> file limit, [then as for truncate, except it is not an error if the
> write starts before the limit, and bytes shall be written if possible
> up to the limit in this case]".  This wording is not very different
> that that for ftruncate, but now it seems even harder to blame the
> write for causing the limit to be exceeded if the write would be in
> the middle of the file.  It seems useful to allow writing in the middle
> of a big file irrespective of the limit, to allow not-fully-trusted
> applications to scribble in a big file that you have reserved for them.
> But the above bug in ftruncate becomes enormous if you allow writing
> in the middle of a big file that the bug has allowed creation of.

Linux doesn't allow writing beyond the limit in a file whose size is
already beyond the limit.  To test this, remove the O_TRUNC and the
ftruncate from the original program, then start with a large file.
Hmm, I was sloppy in testing and might have forgotten to remove the


More information about the freebsd-standards mailing list