Merge branch 'mk/mingw-winansi-ttyname-termination-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Jan 2017 23:19:03 +0000 (15:19 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Jan 2017 23:19:03 +0000 (15:19 -0800)
A potential but unlikely buffer overflow in Windows port has been
fixed.

* mk/mingw-winansi-ttyname-termination-fix:
  mingw: consider that UNICODE_STRING::Length counts bytes

1  2 
compat/winansi.c

diff --combined compat/winansi.c
@@@ -6,12 -6,6 +6,12 @@@
  #include "../git-compat-util.h"
  #include <wingdi.h>
  #include <winreg.h>
 +#include "win32.h"
 +
 +static int fd_is_interactive[3] = { 0, 0, 0 };
 +#define FD_CONSOLE 0x1
 +#define FD_SWAPPED 0x2
 +#define FD_MSYS    0x4
  
  /*
   ANSI codes used by git: m, K
@@@ -87,7 -81,6 +87,7 @@@ static void warn_if_raster_font(void
  static int is_console(int fd)
  {
        CONSOLE_SCREEN_BUFFER_INFO sbi;
 +      DWORD mode;
        HANDLE hcon;
  
        static int initialized = 0;
                return 0;
  
        /* check if its a handle to a console output screen buffer */
 -      if (!GetConsoleScreenBufferInfo(hcon, &sbi))
 +      if (!fd) {
 +              if (!GetConsoleMode(hcon, &mode))
 +                      return 0;
 +      } else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
                return 0;
  
 +      if (fd >= 0 && fd <= 2)
 +              fd_is_interactive[fd] |= FD_CONSOLE;
 +
        /* initialize attributes */
        if (!initialized) {
                console = hcon;
@@@ -472,50 -459,76 +472,50 @@@ static HANDLE duplicate_handle(HANDLE h
        return hresult;
  }
  
 -
 -/*
 - * Make MSVCRT's internal file descriptor control structure accessible
 - * so that we can tweak OS handles and flags directly (we need MSVCRT
 - * to treat our pipe handle as if it were a console).
 - *
 - * We assume that the ioinfo structure (exposed by MSVCRT.dll via
 - * __pioinfo) starts with the OS handle and the flags. The exact size
 - * varies between MSVCRT versions, so we try different sizes until
 - * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
 - * isatty(1).
 - */
 -typedef struct {
 -      HANDLE osfhnd;
 -      char osflags;
 -} ioinfo;
 -
 -extern __declspec(dllimport) ioinfo *__pioinfo[];
 -
 -static size_t sizeof_ioinfo = 0;
 -
 -#define IOINFO_L2E 5
 -#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
 -
 -#define FPIPE 0x08
 -#define FDEV  0x40
 -
 -static inline ioinfo* _pioinfo(int fd)
 -{
 -      return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
 -                      (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
 -}
 -
 -static int init_sizeof_ioinfo()
 -{
 -      int istty, wastty;
 -      /* don't init twice */
 -      if (sizeof_ioinfo)
 -              return sizeof_ioinfo >= 256;
 -
 -      sizeof_ioinfo = sizeof(ioinfo);
 -      wastty = isatty(1);
 -      while (sizeof_ioinfo < 256) {
 -              /* toggle FDEV flag, check isatty, then toggle back */
 -              _pioinfo(1)->osflags ^= FDEV;
 -              istty = isatty(1);
 -              _pioinfo(1)->osflags ^= FDEV;
 -              /* return if we found the correct size */
 -              if (istty != wastty)
 -                      return 0;
 -              sizeof_ioinfo += sizeof(void*);
 -      }
 -      error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
 -      return 1;
 -}
 -
  static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
  {
 -      ioinfo *pioinfo;
 -      HANDLE old_handle;
 -
 -      /* init ioinfo size if we haven't done so */
 -      if (init_sizeof_ioinfo())
 -              return INVALID_HANDLE_VALUE;
 -
 -      /* get ioinfo pointer and change the handles */
 -      pioinfo = _pioinfo(fd);
 -      old_handle = pioinfo->osfhnd;
 -      pioinfo->osfhnd = new_handle;
 -      return old_handle;
 +      /*
 +       * Create a copy of the original handle associated with fd
 +       * because the original will get closed when we dup2().
 +       */
 +      HANDLE handle = (HANDLE)_get_osfhandle(fd);
 +      HANDLE duplicate = duplicate_handle(handle);
 +
 +      /* Create a temp fd associated with the already open "new_handle". */
 +      int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY);
 +
 +      assert((fd == 1) || (fd == 2));
 +
 +      /*
 +       * Use stock dup2() to re-bind fd to the new handle.  Note that
 +       * this will implicitly close(1) and close both fd=1 and the
 +       * originally associated handle.  It will open a new fd=1 and
 +       * call DuplicateHandle() on the handle associated with new_fd.
 +       * It is because of this implicit close() that we created the
 +       * copy of the original.
 +       *
 +       * Note that the OS can recycle HANDLE (numbers) just like it
 +       * recycles fd (numbers), so we must update the cached value
 +       * of "console".  You can use GetFileType() to see that
 +       * handle and _get_osfhandle(fd) may have the same number
 +       * value, but they refer to different actual files now.
 +       *
 +       * Note that dup2() when given target := {0,1,2} will also
 +       * call SetStdHandle(), so we don't need to worry about that.
 +       */
 +      dup2(new_fd, fd);
 +      if (console == handle)
 +              console = duplicate;
 +      handle = INVALID_HANDLE_VALUE;
 +
 +      /* Close the temp fd.  This explicitly closes "new_handle"
 +       * (because it has been associated with it).
 +       */
 +      close(new_fd);
 +
 +      fd_is_interactive[fd] |= FD_SWAPPED;
 +
 +      return duplicate;
  }
  
  #ifdef DETECT_MSYS_TTY
@@@ -540,37 -553,23 +540,37 @@@ static void detect_msys_tty(int fd
                        buffer, sizeof(buffer) - 2, &result)))
                return;
        name = nameinfo->Name.Buffer;
-       name[nameinfo->Name.Length] = 0;
+       name[nameinfo->Name.Length / sizeof(*name)] = 0;
  
 -      /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
 -      if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty"))
 +      /*
 +       * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX')
 +       * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX')
 +       */
 +      if ((!wcsstr(name, L"msys-") && !wcsstr(name, L"cygwin-")) ||
 +                      !wcsstr(name, L"-pty"))
                return;
  
 -      /* init ioinfo size if we haven't done so */
 -      if (init_sizeof_ioinfo())
 -              return;
 -
 -      /* set FDEV flag, reset FPIPE flag */
 -      _pioinfo(fd)->osflags &= ~FPIPE;
 -      _pioinfo(fd)->osflags |= FDEV;
 +      fd_is_interactive[fd] |= FD_MSYS;
  }
  
  #endif
  
 +/*
 + * Wrapper for isatty().  Most calls in the main git code
 + * call isatty(1 or 2) to see if the instance is interactive
 + * and should: be colored, show progress, paginate output.
 + * We lie and give results for what the descriptor WAS at
 + * startup (and ignore any pipe redirection we internally
 + * do).
 + */
 +#undef isatty
 +int winansi_isatty(int fd)
 +{
 +      if (fd >= 0 && fd <= 2)
 +              return fd_is_interactive[fd] != 0;
 +      return isatty(fd);
 +}
 +
  void winansi_init(void)
  {
        int con1, con2;
        /* check if either stdout or stderr is a console output screen buffer */
        con1 = is_console(1);
        con2 = is_console(2);
 +
 +      /* Also compute console bit for fd 0 even though we don't need the result here. */
 +      is_console(0);
 +
        if (!con1 && !con2) {
  #ifdef DETECT_MSYS_TTY
                /* check if stdin / stdout / stderr are MSYS2 pty pipes */
   */
  HANDLE winansi_get_osfhandle(int fd)
  {
 -      HANDLE hnd = (HANDLE) _get_osfhandle(fd);
 -      if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
 -              if (fd == 1 && hconsole1)
 -                      return hconsole1;
 -              else if (fd == 2 && hconsole2)
 -                      return hconsole2;
 -      }
 -      return hnd;
 +      if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
 +              return hconsole1;
 +      if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
 +              return hconsole2;
 +
 +      return (HANDLE)_get_osfhandle(fd);
  }