0

I couldn't find any behavior defined in the man page for read(3). My guess would be that it either throws an error or it just returns 0. But if someone could confirm any defined behavior that would be great. Or if the behavior is specified as being undefined somewhere then stating that would be great as well.

Justin
  • 1

2 Answers2

2

Such a situation cannot happen by definition (at least under POSIX.1-2008 and later). The nbyte argument to read() is a size_t and that's an unsigned integer type.

TooTea
  • 2,388
  • 11
  • 15
1

Consider the prototype of the read function:

ssize_t read(int fd, void *buf, size_t count);

Since the count argument is a size_t which is unsigned, if you try to pass a negative number eg. read(fd, buf, -20) (and you're running a 2's complement machine ;-)) that will be interpreted as a very large count, outside the range of successful return values -- read() returns the number of bytes read as a ssize_t, a signed integer of the same size as size_t.

In that case the following applies:

If the value of nbyte is greater than {SSIZE_MAX}, the result is implementation-defined.

This is on linux:

On Linux, read() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually transferred. (This is true on both 32-bit and 64-bit systems.)

In practice, a read() with a count larger than SSIZE_MAX results in an EFAULT, because it's impossible to fit the memory block starting at buf and extending for count bytes within the address space of the process.

On *bsd:

read() and pread() may return the following error:

[EINVAL] nbytes was larger than SSIZE_MAX

And the same on solaris:

EINVAL

The nbyte argument overflowed an ssize_t.

Note: the openbsd manpage makes mention of systems where read() could return a negative value on success; if you know such a system, please add info about it here or in the comments.

  • 2's complement does not figure in, ISO C defines the result regardless of signed integer representation. – Remember Monica Dec 04 '20 at 20:43
  • On many operating systems, GNU/Linux included, processes can alloate more memory tzhan SSIZE_MAX, so it's not clear why EFAULT would be generated. Additionally, in practise, most operating systems simply reduce the count and simply return fewer octets than requested, which is legal by POSIX (Linux does this for example). – Remember Monica Dec 04 '20 at 20:44
  • @RememberMonica Linux checks before everything if the whole buffer is within the address space of the process, and if not, fails with EFAULT (this is also documented in the manpage; EFAULT could also be returned later, if the buffer is not backed by any memory). Could you give an example of a Linux running machine where you would able to fit a buffer > SSIZE_MAX within the address space of a process? –  Dec 06 '20 at 04:42
  • As to 2's complement, doesn't the ISO C standard basically say (in a roundabout way) that signed -> unsigned int of the same size should happen as if the same bit pattern would be reinterpreted as an unsigned int on a 2's complement machine? –  Dec 06 '20 at 04:44
  • the address space of a 32 bit process can be larger (e.g. 3GB) than the largest ssize_t value (er.g. ~2GB). – Remember Monica Dec 06 '20 at 06:13
  • as for 2's complement, yes, it kind of says that. the point being that the conversion is well defined and the same, regardless of whether it uses 2c or not. – Remember Monica Dec 06 '20 at 06:15