0

Here is a piece of code from the lib/xreadlink.c file in GNU Coreutils..

  /* Call readlink to get the symbolic link value of FILENAME.
   +   SIZE is a hint as to how long the link is expected to be;
   +   typically it is taken from st_size.  It need not be correct.
       Return a pointer to that NUL-terminated string in malloc'd storage.
       If readlink fails, return NULL (caller may use errno to diagnose).
       If malloc fails, or if the link value is longer than SSIZE_MAX :-),
       give a diagnostic and exit.  */

 char * xreadlink (char const *filename)
 {
    /* The initial buffer size for the link value.  A power of 2
       detects arithmetic overflow earlier, but is not required.  */
    size_t buf_size = 128;

    while (1)
    {
      char* buffer = xmalloc(buf_size);
      ssize_t link_length = readlink(filename, buffer, buf_size);
      if(link_length < 0)
      {
         /*handle failure of system call*/
      }

      if((size_t) link_length < buf_size)
      {
           buffer[link_length] = 0;
           return buffer;
      }

      /*size not sufficient, allocate more*/
      free (buffer);
      buf_size *= 2;
      /*Check whether increase is possible*/
      if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
          xalloc_die ();
   }
}

The code is understandable except I could not understand how the check for whether the link's size is too big works, that is the line:

      if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))

Further, how can

     (SIZE_MAX / 2 < SSIZE_MAX) 

condition be true on any system???

bhuwansahni
  • 1,834
  • 1
  • 14
  • 20

1 Answers1

1

SSIZE_MAX is the maximum value of the signed variety of size_t. For instance if size_t is only 16 bits (very unlikely these days), SIZE_MAX is 65535 while ssize_max is 32767. More likely it is 32 bits (giving 4294967295 and 2147483647 respectively), or even 64 bits (giving numbers too big to type here :-) ).

The basic problem to solve here is that readlink returns a signed value even though SIZE_MAX is an unsigned one ... so once buf_size exceeds SSIZE_MAX, it's impossible to read the link, as the large positive value will result in a negative return value.

As for the "furthermore" part: it quite likely can't, i.e., you're right. At least on any sane system, anyway. (It is theoretically possible to have, e.g., a 32-bit SIZE_MAX but a 33-bit signed integer so that SSIZE_MAX is also 4294967295. Presumably this code is written to guard against theoretically-possible, but never-actually-seen, systems.)

torek
  • 448,244
  • 59
  • 642
  • 775