1. 14 9月, 2017 3 次提交
    • J
      avoid "write_in_full(fd, buf, len) != len" pattern · 06f46f23
      Jeff King 提交于
      The return value of write_in_full() is either "-1", or the
      requested number of bytes[1]. If we make a partial write
      before seeing an error, we still return -1, not a partial
      value. This goes back to f6aa66cb (write_in_full: really
      write in full or return error on disk full., 2007-01-11).
      
      So checking anything except "was the return value negative"
      is pointless. And there are a couple of reasons not to do
      so:
      
        1. It can do a funny signed/unsigned comparison. If your
           "len" is signed (e.g., a size_t) then the compiler will
           promote the "-1" to its unsigned variant.
      
           This works out for "!= len" (unless you really were
           trying to write the maximum size_t bytes), but is a
           bug if you check "< len" (an example of which was fixed
           recently in config.c).
      
           We should avoid promoting the mental model that you
           need to check the length at all, so that new sites are
           not tempted to copy us.
      
        2. Checking for a negative value is shorter to type,
           especially when the length is an expression.
      
        3. Linus says so. In d34cf19b (Clean up write_in_full()
           users, 2007-01-11), right after the write_in_full()
           semantics were changed, he wrote:
      
             I really wish every "write_in_full()" user would just
             check against "<0" now, but this fixes the nasty and
             stupid ones.
      
           Appeals to authority aside, this makes it clear that
           writing it this way does not have an intentional
           benefit. It's a historical curiosity that we never
           bothered to clean up (and which was undoubtedly
           cargo-culted into new sites).
      
      So let's convert these obviously-correct cases (this
      includes write_str_in_full(), which is just a wrapper for
      write_in_full()).
      
      [1] A careful reader may notice there is one way that
          write_in_full() can return a different value. If we ask
          write() to write N bytes and get a return value that is
          _larger_ than N, we could return a larger total. But
          besides the fact that this would imply a totally broken
          version of write(), it would already invoke undefined
          behavior. Our internal remaining counter is an unsigned
          size_t, which means that subtracting too many byte will
          wrap it around to a very large number. So we'll instantly
          begin reading off the end of the buffer, trying to write
          gigabytes (or petabytes) of data.
      Signed-off-by: NJeff King <peff@peff.net>
      Reviewed-by: NJonathan Nieder <jrnieder@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      06f46f23
    • J
      get-tar-commit-id: check write_in_full() return against 0 · 68a423ab
      Jeff King 提交于
      We ask to write 41 bytes and make sure that the return value
      is at least 41. This is the same "dangerous" pattern that
      was fixed in the prior commit (wherein a negative return
      value is promoted to unsigned), though it is not dangerous
      here because our "41" is a constant, not an unsigned
      variable.
      
      But we should convert it anyway to avoid modeling a
      dangerous construct.
      Signed-off-by: NJeff King <peff@peff.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      68a423ab
    • J
      config: avoid "write_in_full(fd, buf, len) < len" pattern · efacf609
      Jeff King 提交于
      The return type of write_in_full() is a signed ssize_t,
      because we may return "-1" on failure (even if we succeeded
      in writing some bytes). But "len" itself is may be an
      unsigned type (the function takes a size_t, but of course we
      may have something else in the calling function). So while
      it seems like:
      
        if (write_in_full(fd, buf, len) < len)
      	die_errno("write error");
      
      would trigger on error, it won't if "len" is unsigned.  The
      compiler sees a signed/unsigned comparison and promotes the
      signed value, resulting in (size_t)-1, the highest possible
      size_t (or again, whatever type the caller has). This cannot
      possibly be smaller than "len", and so the conditional can
      never trigger.
      
      I scoured the code base for cases of this, but it turns out
      that these two in git_config_set_multivar_in_file_gently()
      are the only ones. Here our "len" is the difference between
      two size_t variables, making the result an unsigned size_t.
      We can fix this by just checking for a negative return value
      directly, as write_in_full() will never return any value
      except -1 or the full count.
      
      There's no addition to the test suite here, since you need
      to convince write() to fail in order to see the problem. The
      simplest reproduction recipe I came up with is to trigger
      ENOSPC:
      
        # make a limited-size filesystem
        dd if=/dev/zero of=small.disk bs=1M count=1
        mke2fs small.disk
        mkdir mnt
        sudo mount -o loop small.disk mnt
        cd mnt
        sudo chown $USER:$USER .
      
        # make a config file with some content
        git config --file=config one.key value
        git config --file=config two.key value
      
        # now fill up the disk
        dd if=/dev/zero of=fill
      
        # and try to delete a key, which requires copying the rest
        # of the file to config.lock, and will fail on write()
        git config --file=config --unset two.key
      
      That final command should (and does after this patch)
      produce an error message due to the failed write, and leave
      the file intact. Instead, it silently ignores the failure
      and renames config.lock into place, leaving you with a
      totally empty config file!
      Reported-by: Ndemerphq <demerphq@gmail.com>
      Signed-off-by: NJeff King <peff@peff.net>
      Reviewed-by: NJonathan Nieder <jrnieder@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      efacf609
  2. 10 9月, 2017 37 次提交