0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

プロキシ環境下でGit for Windowsが自動アップデートできないバグを直してもらった話

Posted at

前置き

tl;dr

バグについて

背景

  • Git for Windowsは、Windows版のGitとしてGitの公式サイトなどからダウンロードできるパッケージ
    • MSYS2を使ってGitが動くために必要なPerlやTcl/Tk等のパッケージ群をまとめたもの
  • 他のOSと違ってパッケージマネージャやOSによるアップデートがかからないので、自動アップデート機能がついている
  • この自動アップデート機能はcurlによって更新チェック・ダウンロードを行うシェルスクリプトで実現されている
  • curlにOSのプロキシ設定を反映するため、proxy-lookupというWindows APIを使用したプログラムでOSにプロキシ設定を問い合わせている

発見の経緯

  • 会社のPCにGit for Windowsをインストールした際、自動アップデート機能があると気づいた
  • 会社ネットワークはプロキシを経由しないとインターネットにアクセスできない環境なので動かないだろうと思って試したら、やはり動かなかった
  • セキュリティ上自動アップデートは無効化することにこだわるべきでもないと思い、プロキシの設定をしてみようと考えた1
  • 設定方法がわからないのでとりあえずソースを読んでみたところ、proxy-lookupによってOSのプロキシ設定を取得していることがわかった
  • なぜ動かないのかと思ってproxy-lookupが何を返しているのか確認してみた
    $ proxy-lookup http://gitforwindows.org/
    p
    
    • なんかよくわからんが壊れているということはわかった

調査

  • proxy-lookupのソースを探してダウンロードして読んでみたが問題に気づかなかった1
    MSVCでコンパイルしてみても2正しく動いているようだった
    $ proxy-lookup http://gitforwindows.org/
    proxy.example.com:xxxx
    
  • 気になって自宅に帰ってから調べてみた。Git for Windowsのビルドに使われているというGit for Windows SDKをダウンロードしてこれでビルドしてみた
    • MSVCだと起こらないが、Git for Windows SDKでビルドするとこの問題が起こるようだった
  • 挙動を見ていると、proxy.pacを使って特定ホストにだけ違うプロキシを返させた場合、表示がきちんと変わることが分かった
    • proxy.pac
      function FindProxyForURL(url, host) {
          if (isInNet(host, "192.168.0.0", "255.255.255.0")) {
            return "PROXY 192.168.0.131:8080";
          }
          return "DIRECT";
      }
      
    • 実行結果
      $ proxy-lookup -v http://192.168.0.1/
      URL: h, proxy: 1
      $ proxy-lookup -v http://192.168.1.1/
      URL: h, proxy: 
      
    • ソースコード3を見ると、結果のproxyはともかくURLはget_proxy_for_urlにすら関係しない。
      wprintfがうまく動いていないことを疑った
      int main(int argc, char **argv)
      {
      	LPWSTR *wargv;
      	int i, wargc, verbose = 0;
      
      	wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
      
      	for (i = 1; i < wargc; i++) {
      		LPCWSTR arg = wargv[i], proxy;
      
       		if (!wcscmp(L"--verbose", arg) || !wcscmp(L"-v", arg)) {
      			verbose = 1;
      			continue;
      		}
      
      		proxy = get_proxy_for_url(arg);
      		if (!proxy)
      			proxy = L"";
      
      		if (verbose)
      			wprintf(L"URL: %s, proxy: %s\n", arg, proxy);
      		else
      			wprintf(L"%s\n", proxy);
      	}
      
      	return 0;
      }
      
  • Git for Windows SDKでデバッグビルドしてステップ実行してみると、wprintfの内部にまで入っていくことができたので、そのまま実行を進めた。すると、次に示す処理が動いていた...による省略は引用者による)4
    int
    __pformat (int flags, void *dest, int max, const APICHAR *fmt, va_list argv)
    {
      int c;
      // ...
      format_scan: while( (c = *fmt++) != 0 )
      {
        // ...
        if( c == '%' )
        {
          // ...
          __pformat_length_t length = PFORMAT_LENGTH_INT;
          // ...
          while( *fmt )
          {
            switch( c = *fmt++ )
            {
              // ...
              case 's':
                if( (length == PFORMAT_LENGTH_LONG)
                     || (length == PFORMAT_LENGTH_LLONG))
                {
                  // ...
                  __pformat_wcputs( va_arg( argv, wchar_t * ), &stream );
                }
                else
                  /* ... */
                  __pformat_puts( va_arg( argv, char * ), &stream );
                goto format_scan;
    
    • この関数はAPICHARというマクロを使ってwprintfprintfで共有されているが、
      lengthの指定がついていない%sはその両方で(APICHAR*ではなく)char *として処理されていることが読み取れた
  • %sの解釈について調べると、cppreference.comなどにはchar *として扱うと書かれていたが、Microsoftは%sの意味をprintfならchar*だが、wprintfならwchar_t*とする独自拡張を入れていることが分かった。MinGW-w64では独自拡張を反映していないのだろうと思った。

報告

追加の調査

結論

  • 今回私が気にした「いつどの変更によってこの問題がトリガーされたか」は、上記の通り「2020年のこのコミット」が答えである
  1. 休憩時間に行っています 2 3

  2. フリーソフトの使用は注意して行うという規則はありますが、Git for Windowsは社内で過去に使用したことがあるので問題ありません

  3. このソースコードはパブリックドメインに置かれています

  4. この記事の内容に必要な引用と考えています

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?