• R/O
  • SSH
  • HTTPS

ttssh2: Commit


Commit MetaInfo

Revision7771 (tree)
Time2019-06-15 15:42:23
Author(del#24082)

Log Message

ポート転送でSSH通信が遅い場合において、Tera Term(TTSSH)の消費メモリが
肥大化して、アプリが落ちる問題を修正した。
チケット #39297

local channelからのパケット読み込み時、SSHサーバへの送信でremote_windowに
空きがない場合は、バッファにパケットを記録するが、フロー制御を追加して際限なく
バッファに溜め込まないようにした。

branches/portfwd_memleak ブランチからのマージ。

Change Summary

Incremental Difference

--- trunk/ttssh2/ttxssh/fwd.c (revision 7770)
+++ trunk/ttssh2/ttxssh/fwd.c (revision 7771)
@@ -679,9 +679,21 @@
679679
680680 while (channel->local_socket != INVALID_SOCKET) {
681681 char buf[CHANNEL_READ_BUF_SIZE];
682- int amount = recv(channel->local_socket, buf, sizeof(buf), 0);
682+ int amount;
683683 int err;
684684
685+ // recvの一時停止中ならば、何もせずに戻る。
686+ if (SSHv2(pvar)) {
687+ Channel_t* c = ssh2_local_channel_lookup(channel_num);
688+ if (c->bufchain_recv_suspended) {
689+ logprintf(LOG_LEVEL_NOTICE, "%s: channel=%d recv was skipped for flow control",
690+ __FUNCTION__, channel_num);
691+ return;
692+ }
693+ }
694+
695+ amount = recv(channel->local_socket, buf, sizeof(buf), 0);
696+
685697 // Xサーバからのデータ受信があれば、ノンブロッキングモードでソケット受信を行い、
686698 // SSHサーバのXアプリケーションへ送信する。
687699 //OutputDebugPrintf("%s: recv %d\n", __FUNCTION__, amount);
@@ -740,6 +752,63 @@
740752 }
741753 }
742754
755+// local connectionの受信の停止および再開の判断を行う
756+//
757+// notify: TRUE recvを再開する
758+// FALSE recvを停止する
759+//
760+// [目的]
761+// remote_windowに空きがない場合は通知オフとし、空きができた場合は
762+// 通知を再開する。
763+// remote_windowに余裕がない状態で、local connectionからのパケットを
764+// 受信し続けると、消費メモリが肥大化する(厳密にはメモリリークではない)
765+// という問題を回避する。
766+//
767+// (2019.6.5 yutaka)
768+void FWD_suspend_resume_local_connection(PTInstVar pvar, Channel_t* c, int notify)
769+{
770+ int channel_num;
771+ FWDChannel* channel;
772+ int changed = 0;
773+
774+ channel_num = c->local_num;
775+ channel = pvar->fwd_state.channels + channel_num;
776+
777+ if (notify) {
778+ // recvを再開するか判断する
779+ if (c->bufchain_amount <= FWD_LOW_WATER_MARK) {
780+ // 下限を下回ったので再開
781+ c->bufchain_recv_suspended = FALSE;
782+
783+ // ここで再開のメッセージを飛ばす
784+ PostMessage(pvar->fwd_state.accept_wnd, WM_SOCK_IO,
785+ (WPARAM)channel->local_socket,
786+ MAKEWPARAM(FD_READ, 0)
787+ );
788+
789+ changed = 1;
790+ }
791+
792+ } else {
793+ // recvを停止するか判断する
794+ if (c->bufchain_amount >= FWD_HIGH_WATER_MARK) {
795+ // 上限を超えたので停止
796+ c->bufchain_recv_suspended = TRUE;
797+ changed = 1;
798+ }
799+ }
800+
801+ logprintf(LOG_LEVEL_NOTICE,
802+ "%s: Local channel#%d recv has been `%s' for flow control(buffer size %lu, recv %s).",
803+ __FUNCTION__, channel_num,
804+ c->bufchain_recv_suspended ? "disabled" : "enabled",
805+ c->bufchain_amount,
806+ changed ? "changed" : ""
807+ );
808+
809+}
810+
811+
743812 static LRESULT CALLBACK accept_wnd_proc(HWND wnd, UINT msg, WPARAM wParam,
744813 LPARAM lParam)
745814 {
--- trunk/ttssh2/ttxssh/fwd.h (revision 7770)
+++ trunk/ttssh2/ttxssh/fwd.h (revision 7771)
@@ -35,6 +35,11 @@
3535 #ifndef __FWD_H
3636 #define __FWD_H
3737
38+// ポート転送におけるフロー制御の閾値
39+// 適用先 Channel_t.bufchain_amount
40+#define FWD_HIGH_WATER_MARK (1 * 1024 * 1024) // 1MB
41+#define FWD_LOW_WATER_MARK (0) // 0MB
42+
3843 #define FWD_REMOTE_CONNECTED 0x01
3944 #define FWD_LOCAL_CONNECTED 0x02
4045 #define FWD_BOTH_CONNECTED (FWD_REMOTE_CONNECTED | FWD_LOCAL_CONNECTED)
@@ -164,5 +169,6 @@
164169 int FWD_check_local_channel_num(PTInstVar pvar, int local_num);
165170 int FWD_agent_open(PTInstVar pvar, uint32 remote_channel_num);
166171 BOOL FWD_agent_forward_confirm(PTInstVar pvar);
172+void FWD_suspend_resume_local_connection(PTInstVar pvar, Channel_t* c, int notify);
167173
168174 #endif
--- trunk/ttssh2/ttxssh/ssh.c (revision 7770)
+++ trunk/ttssh2/ttxssh/ssh.c (revision 7771)
@@ -213,6 +213,8 @@
213213 c->type = type;
214214 c->local_num = local_num; // alloc_channel()の返値を保存しておく
215215 c->bufchain = NULL;
216+ c->bufchain_amount = 0;
217+ c->bufchain_recv_suspended = FALSE;
216218 if (type == TYPE_SCP) {
217219 c->scp.state = SCP_INIT;
218220 c->scp.progress_window = NULL;
@@ -231,7 +233,8 @@
231233 }
232234
233235 // remote_windowの空きがない場合に、送れなかったバッファをリスト(入力順)へつないでおく。
234-static void ssh2_channel_add_bufchain(Channel_t *c, unsigned char *buf, unsigned int buflen)
236+// ここで確保したメモリは ssh2_channel_retry_send_bufchain() で解放する。
237+static void ssh2_channel_add_bufchain(PTInstVar pvar, Channel_t *c, unsigned char *buf, unsigned int buflen)
235238 {
236239 bufchain_t *p, *old;
237240
@@ -255,12 +258,22 @@
255258 old = old->next;
256259 old->next = p;
257260 }
261+
262+ // バッファサイズの合計を更新する(記録用)
263+ c->bufchain_amount += buflen;
264+
265+ // remote_windowの空きがないので、local connectionからのパケット受信の
266+ // 停止指示を出す。すぐに通知が止まるわけではない。
267+ FWD_suspend_resume_local_connection(pvar, c, FALSE);
258268 }
259269
270+// remote_windowの空きができたら、リストに残っているデータを順番に送る。
271+// 送信ができたらメモリを解放する。
260272 static void ssh2_channel_retry_send_bufchain(PTInstVar pvar, Channel_t *c)
261273 {
262274 bufchain_t *ch;
263275 unsigned int size;
276+ bufchain_t* ch_origin = c->bufchain;
264277
265278 while (c->bufchain) {
266279 // 先頭から先に送る
@@ -279,7 +292,16 @@
279292
280293 buffer_free(ch->msg);
281294 free(ch);
295+
296+ // バッファサイズの合計を更新する(記録用)
297+ c->bufchain_amount -= size;
282298 }
299+
300+ // 元々あったリストが空になったら、
301+ // local connectionからのパケット通知を再開する。
302+ if (ch_origin && c->bufchain == NULL) {
303+ FWD_suspend_resume_local_connection(pvar, c, TRUE);
304+ }
283305 }
284306
285307 // channel close時にチャネル構造体をリストへ返却する
@@ -370,7 +392,7 @@
370392 // SSH1で管理しているchannel構造体から、SSH2向けのChannel_tへ変換する。
371393 // TODO: 将来的にはチャネル構造体は1つに統合する。
372394 // (2005.6.12 yutaka)
373-static Channel_t *ssh2_local_channel_lookup(int local_num)
395+Channel_t *ssh2_local_channel_lookup(int local_num)
374396 {
375397 int i;
376398 Channel_t *c;
@@ -3441,7 +3463,7 @@
34413463 // これによりパケットが壊れたように見える現象が改善される。
34423464 // (2012.10.14 yutaka)
34433465 if (retry == 0 && c->bufchain) {
3444- ssh2_channel_add_bufchain(c, buf, buflen);
3466+ ssh2_channel_add_bufchain(pvar, c, buf, buflen);
34453467 return;
34463468 }
34473469
@@ -3448,7 +3470,7 @@
34483470 if ((unsigned int)buflen > c->remote_window) {
34493471 unsigned int offset = 0;
34503472 // 送れないデータはいったん保存しておく
3451- ssh2_channel_add_bufchain(c, buf + offset, buflen - offset);
3473+ ssh2_channel_add_bufchain(pvar, c, buf + offset, buflen - offset);
34523474 buflen = offset;
34533475 return;
34543476 }
--- trunk/ttssh2/ttxssh/ssh.h (revision 7770)
+++ trunk/ttssh2/ttxssh/ssh.h (revision 7771)
@@ -881,6 +881,8 @@
881881 enum channel_type type;
882882 int local_num;
883883 bufchain_t *bufchain;
884+ unsigned long bufchain_amount;
885+ BOOL bufchain_recv_suspended;
884886 scp_t scp;
885887 buffer_t *agent_msg;
886888 int agent_request_len;
@@ -892,6 +894,7 @@
892894 unsigned char *begin_send_packet(PTInstVar pvar, int type, int len);
893895 void finish_send_packet_special(PTInstVar pvar, int skip_compress);
894896 void SSH2_send_channel_data(PTInstVar pvar, Channel_t *c, unsigned char *buf, unsigned int buflen, int retry);
897+Channel_t* ssh2_local_channel_lookup(int local_num);
895898
896899 #define finish_send_packet(pvar) finish_send_packet_special((pvar), 0)
897900 #define get_payload_uint32(pvar, offset) get_uint32_MSBfirst((pvar)->ssh_state.payload + (offset))
Show on old repository browser