From owner-freebsd-standards@FreeBSD.ORG Thu Feb 16 16:32:47 2012 Return-Path: Delivered-To: freebsd-standards@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2BFA8106564A for ; Thu, 16 Feb 2012 16:32:47 +0000 (UTC) (envelope-from brde@optusnet.com.au) Received: from mail01.syd.optusnet.com.au (mail01.syd.optusnet.com.au [211.29.132.182]) by mx1.freebsd.org (Postfix) with ESMTP id B857C8FC17 for ; Thu, 16 Feb 2012 16:32:46 +0000 (UTC) Received: from c211-30-171-136.carlnfd1.nsw.optusnet.com.au (c211-30-171-136.carlnfd1.nsw.optusnet.com.au [211.30.171.136]) by mail01.syd.optusnet.com.au (8.13.1/8.13.1) with ESMTP id q1GGWgdR030271 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 17 Feb 2012 03:32:44 +1100 Date: Fri, 17 Feb 2012 03:32:42 +1100 (EST) From: Bruce Evans X-X-Sender: bde@besplex.bde.org To: Bruce Evans In-Reply-To: <20120216054457.H3935@besplex.bde.org> Message-ID: <20120217031824.R760@besplex.bde.org> References: <4F3BC2DB.6080703@gmail.com> <20120215163800.GA3283@deviant.kiev.zoral.com.ua> <20120216054457.H3935@besplex.bde.org> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed Cc: freebsd-standards@freebsd.org, Nicolas Bourdaud Subject: Re: write system call violates POSIX standard X-BeenThere: freebsd-standards@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Standards compliance List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 16 Feb 2012 16:32:47 -0000 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 > #include > > #include > #include > #include > #include > #include > > #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 ftruncate. Bruce