Revision: 7771 https://osdn.net/projects/ttssh2/scm/svn/commits/7771 Author: yutakapon Date: 2019-06-15 15:42:23 +0900 (Sat, 15 Jun 2019) Log Message: ----------- ポート転送でSSH通信が遅い場合において、Tera Term(TTSSH)の消費メモリが 肥大化して、アプリが落ちる問題を修正した。 チケット #39297 local channelからのパケット読み込み時、SSHサーバへの送信でremote_windowに 空きがない場合は、バッファにパケットを記録するが、フロー制御を追加して際限なく バッファに溜め込まないようにした。 branches/portfwd_memleak ブランチからのマージ。 Ticket Links: ------------ https://osdn.net/projects/ttssh2/tracker/detail/39297 Modified Paths: -------------- trunk/ttssh2/ttxssh/fwd.c trunk/ttssh2/ttxssh/fwd.h trunk/ttssh2/ttxssh/ssh.c trunk/ttssh2/ttxssh/ssh.h Property Changed: ---------------- trunk/ -------------- next part -------------- Index: trunk =================================================================== --- trunk 2019-06-14 23:40:58 UTC (rev 7770) +++ trunk 2019-06-15 06:42:23 UTC (rev 7771) Property changes on: trunk ___________________________________________________________________ Modified: svn:mergeinfo ## -1,4 +1,5 ## /branches/drag_and_drop:7130,7139,7141,7143-7147 +/branches/portfwd_memleak:7732,7736-7737,7764 /branches/ssh_ed25519:5495-5544 /branches/vs2015_warn:6194-6285 /tags/teraterm-4_89:6182 \ No newline at end of property Modified: trunk/ttssh2/ttxssh/fwd.c =================================================================== --- trunk/ttssh2/ttxssh/fwd.c 2019-06-14 23:40:58 UTC (rev 7770) +++ trunk/ttssh2/ttxssh/fwd.c 2019-06-15 06:42:23 UTC (rev 7771) @@ -679,9 +679,21 @@ while (channel->local_socket != INVALID_SOCKET) { char buf[CHANNEL_READ_BUF_SIZE]; - int amount = recv(channel->local_socket, buf, sizeof(buf), 0); + int amount; int err; + // recv\x82̈ꎞ\x92\xE2\x8E~\x92\x86\x82Ȃ\xE7\x82A\x89\xBD\x82\xE0\x82\xB9\x82\xB8\x82ɖ߂\xE9\x81B + if (SSHv2(pvar)) { + Channel_t* c = ssh2_local_channel_lookup(channel_num); + if (c->bufchain_recv_suspended) { + logprintf(LOG_LEVEL_NOTICE, "%s: channel=%d recv was skipped for flow control", + __FUNCTION__, channel_num); + return; + } + } + + amount = recv(channel->local_socket, buf, sizeof(buf), 0); + // X\x83T\x81[\x83o\x82\xA9\x82\xE7\x82̃f\x81[\x83^\x8E\xF3\x90M\x82\xAA\x82\xA0\x82\xEA\x82A\x83m\x83\x93\x83u\x83\x8D\x83b\x83L\x83\x93\x83O\x83\x82\x81[\x83h\x82Ń\\x83P\x83b\x83g\x8E\xF3\x90M\x82\xF0\x8Ds\x82\xA2\x81A // SSH\x83T\x81[\x83o\x82\xCCX\x83A\x83v\x83\x8A\x83P\x81[\x83V\x83\x87\x83\x93\x82֑\x97\x90M\x82\xB7\x82\xE9\x81B //OutputDebugPrintf("%s: recv %d\n", __FUNCTION__, amount); @@ -740,6 +752,63 @@ } } +// local connection\x82̎\xF3\x90M\x82̒\xE2\x8E~\x82\xA8\x82\xE6\x82эĊJ\x82̔\xBB\x92f\x82\xF0\x8Ds\x82\xA4 +// +// notify: TRUE recv\x82\xF0\x8DĊJ\x82\xB7\x82\xE9 +// FALSE recv\x82\xF0\x92\xE2\x8E~\x82\xB7\x82\xE9 +// +// [\x96ړI] +// remote_window\x82ɋ\xAA\x82Ȃ\xA2\x8Fꍇ\x82͒ʒm\x83I\x83t\x82Ƃ\xB5\x81A\x8B\xAA\x82ł\xAB\x82\xBD\x8Fꍇ\x82\xCD +// \x92ʒm\x82\xF0\x8DĊJ\x82\xB7\x82\xE9\x81B +// remote_window\x82ɗ]\x97T\x82\xAA\x82Ȃ\xA2\x8F\xF3\x91ԂŁAlocal connection\x82\xA9\x82\xE7\x82̃p\x83P\x83b\x83g\x82\xF0 +// \x8E\xF3\x90M\x82\xB5\x91\xB1\x82\xAF\x82\xE9\x82ƁA\x8F\xC1\x94\x83\x82\x83\x8A\x82\xAA\x94\xEC\x91剻\x82\xB7\x82\xE9(\x8C\xB5\x96\xA7\x82ɂ̓\x81\x83\x82\x83\x8A\x83\x8A\x81[\x83N\x82ł͂Ȃ\xA2) +// \x82Ƃ\xA2\x82\xA4\x96\xE2\x91\xE8\x82\xF0\x89\xF1\x94\xF0\x82\xB7\x82\xE9\x81B +// +// (2019.6.5 yutaka) +void FWD_suspend_resume_local_connection(PTInstVar pvar, Channel_t* c, int notify) +{ + int channel_num; + FWDChannel* channel; + int changed = 0; + + channel_num = c->local_num; + channel = pvar->fwd_state.channels + channel_num; + + if (notify) { + // recv\x82\xF0\x8DĊJ\x82\xB7\x82邩\x94\xBB\x92f\x82\xB7\x82\xE9 + if (c->bufchain_amount <= FWD_LOW_WATER_MARK) { + // \x89\xBA\x8C\xC0\x82\xF0\x89\xBA\x89\xF1\x82\xC1\x82\xBD\x82̂ōĊJ + c->bufchain_recv_suspended = FALSE; + + // \x82\xB1\x82\xB1\x82ōĊJ\x82̃\x81\x83b\x83Z\x81[\x83W\x82\xF0\x94\xF2\x82\xB7 + PostMessage(pvar->fwd_state.accept_wnd, WM_SOCK_IO, + (WPARAM)channel->local_socket, + MAKEWPARAM(FD_READ, 0) + ); + + changed = 1; + } + + } else { + // recv\x82\xF0\x92\xE2\x8E~\x82\xB7\x82邩\x94\xBB\x92f\x82\xB7\x82\xE9 + if (c->bufchain_amount >= FWD_HIGH_WATER_MARK) { + // \x8F\xE3\x8C\xC0\x82\xA6\x82\xBD\x82̂Œ\xE2\x8E~ + c->bufchain_recv_suspended = TRUE; + changed = 1; + } + } + + logprintf(LOG_LEVEL_NOTICE, + "%s: Local channel#%d recv has been `%s' for flow control(buffer size %lu, recv %s).", + __FUNCTION__, channel_num, + c->bufchain_recv_suspended ? "disabled" : "enabled", + c->bufchain_amount, + changed ? "changed" : "" + ); + +} + + static LRESULT CALLBACK accept_wnd_proc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { Modified: trunk/ttssh2/ttxssh/fwd.h =================================================================== --- trunk/ttssh2/ttxssh/fwd.h 2019-06-14 23:40:58 UTC (rev 7770) +++ trunk/ttssh2/ttxssh/fwd.h 2019-06-15 06:42:23 UTC (rev 7771) @@ -35,6 +35,11 @@ #ifndef __FWD_H #define __FWD_H +// \x83|\x81[\x83g\x93]\x91\x97\x82ɂ\xA8\x82\xAF\x82\xE9\x83t\x83\x8D\x81[\x90\xA7\x8C\xE4\x82\xCC臒l +// \x93K\x97p\x90\xE6 Channel_t.bufchain_amount +#define FWD_HIGH_WATER_MARK (1 * 1024 * 1024) // 1MB +#define FWD_LOW_WATER_MARK (0) // 0MB + #define FWD_REMOTE_CONNECTED 0x01 #define FWD_LOCAL_CONNECTED 0x02 #define FWD_BOTH_CONNECTED (FWD_REMOTE_CONNECTED | FWD_LOCAL_CONNECTED) @@ -164,5 +169,6 @@ int FWD_check_local_channel_num(PTInstVar pvar, int local_num); int FWD_agent_open(PTInstVar pvar, uint32 remote_channel_num); BOOL FWD_agent_forward_confirm(PTInstVar pvar); +void FWD_suspend_resume_local_connection(PTInstVar pvar, Channel_t* c, int notify); #endif Modified: trunk/ttssh2/ttxssh/ssh.c =================================================================== --- trunk/ttssh2/ttxssh/ssh.c 2019-06-14 23:40:58 UTC (rev 7770) +++ trunk/ttssh2/ttxssh/ssh.c 2019-06-15 06:42:23 UTC (rev 7771) @@ -213,6 +213,8 @@ c->type = type; c->local_num = local_num; // alloc_channel()\x82̕Ԓl\x82\xF0\x95ۑ\xB6\x82\xB5\x82Ă\xA8\x82\xAD c->bufchain = NULL; + c->bufchain_amount = 0; + c->bufchain_recv_suspended = FALSE; if (type == TYPE_SCP) { c->scp.state = SCP_INIT; c->scp.progress_window = NULL; @@ -231,7 +233,8 @@ } // remote_window\x82̋\xAA\x82Ȃ\xA2\x8Fꍇ\x82ɁA\x91\x97\x82\xEA\x82Ȃ\xA9\x82\xC1\x82\xBD\x83o\x83b\x83t\x83@\x82\xF0\x83\x8A\x83X\x83g\x81i\x93\xFC\x97͏\x87\x81j\x82ւȂ\xA2\x82ł\xA8\x82\xAD\x81B -static void ssh2_channel_add_bufchain(Channel_t *c, unsigned char *buf, unsigned int buflen) +// \x82\xB1\x82\xB1\x82Ŋm\x95ۂ\xB5\x82\xBD\x83\x81\x83\x82\x83\x8A\x82\xCD ssh2_channel_retry_send_bufchain() \x82ʼn\xF0\x95\xFA\x82\xB7\x82\xE9\x81B +static void ssh2_channel_add_bufchain(PTInstVar pvar, Channel_t *c, unsigned char *buf, unsigned int buflen) { bufchain_t *p, *old; @@ -255,12 +258,22 @@ old = old->next; old->next = p; } + + // \x83o\x83b\x83t\x83@\x83T\x83C\x83Y\x82̍\x87\x8Cv\x82\xF0\x8DX\x90V\x82\xB7\x82\xE9(\x8BL\x98^\x97p) + c->bufchain_amount += buflen; + + // remote_window\x82̋\xAA\x82Ȃ\xA2\x82̂ŁAlocal connection\x82\xA9\x82\xE7\x82̃p\x83P\x83b\x83g\x8E\xF3\x90M\x82\xCC + // \x92\xE2\x8E~\x8Ew\x8E\xA6\x82\xF0\x8Fo\x82\xB7\x81B\x82\xB7\x82\xAE\x82ɒʒm\x82\xAA\x8E~\x82܂\xE9\x82킯\x82ł͂Ȃ\xA2\x81B + FWD_suspend_resume_local_connection(pvar, c, FALSE); } +// remote_window\x82̋\xAA\x82ł\xAB\x82\xBD\x82\xE7\x81A\x83\x8A\x83X\x83g\x82Ɏc\x82\xC1\x82Ă\xA2\x82\xE9\x83f\x81[\x83^\x82\xF0\x8F\x87\x94Ԃɑ\x97\x82\xE9\x81B +// \x91\x97\x90M\x82\xAA\x82ł\xAB\x82\xBD\x82烁\x83\x82\x83\x8A\x82\xF0\x89\xF0\x95\xFA\x82\xB7\x82\xE9\x81B static void ssh2_channel_retry_send_bufchain(PTInstVar pvar, Channel_t *c) { bufchain_t *ch; unsigned int size; + bufchain_t* ch_origin = c->bufchain; while (c->bufchain) { // \x90擪\x82\xA9\x82\xE7\x90\xE6\x82ɑ\x97\x82\xE9 @@ -279,7 +292,16 @@ buffer_free(ch->msg); free(ch); + + // \x83o\x83b\x83t\x83@\x83T\x83C\x83Y\x82̍\x87\x8Cv\x82\xF0\x8DX\x90V\x82\xB7\x82\xE9(\x8BL\x98^\x97p) + c->bufchain_amount -= size; } + + // \x8C\xB3\x81X\x82\xA0\x82\xC1\x82\xBD\x83\x8A\x83X\x83g\x82\xAA\x8B\xF3\x82ɂȂ\xC1\x82\xBD\x82\xE7\x81A + // local connection\x82\xA9\x82\xE7\x82̃p\x83P\x83b\x83g\x92ʒm\x82\xF0\x8DĊJ\x82\xB7\x82\xE9\x81B + if (ch_origin && c->bufchain == NULL) { + FWD_suspend_resume_local_connection(pvar, c, TRUE); + } } // channel close\x8E\x9E\x82Ƀ`\x83\x83\x83l\x83\x8B\x8D\\x91\xA2\x91̂\xF0\x83\x8A\x83X\x83g\x82֕ԋp\x82\xB7\x82\xE9 @@ -370,7 +392,7 @@ // SSH1\x82ŊǗ\x9D\x82\xB5\x82Ă\xA2\x82\xE9channel\x8D\\x91\xA2\x91̂\xA9\x82\xE7\x81ASSH2\x8C\xFC\x82\xAF\x82\xCCChannel_t\x82֕ϊ\xB7\x82\xB7\x82\xE9\x81B // TODO: \x8F\xAB\x97\x88\x93I\x82ɂ̓`\x83\x83\x83l\x83\x8B\x8D\\x91\xA2\x91̂\xCD1\x82ɓ\x9D\x8D\x87\x82\xB7\x82\xE9\x81B // (2005.6.12 yutaka) -static Channel_t *ssh2_local_channel_lookup(int local_num) +Channel_t *ssh2_local_channel_lookup(int local_num) { int i; Channel_t *c; @@ -3441,7 +3463,7 @@ // \x82\xB1\x82\xEA\x82ɂ\xE6\x82\xE8\x83p\x83P\x83b\x83g\x82\xAA\x89\xF3\x82ꂽ\x82悤\x82Ɍ\xA9\x82\xA6\x82錻\x8Fۂ\xAA\x89\xFC\x91P\x82\xB3\x82\xEA\x82\xE9\x81B // (2012.10.14 yutaka) if (retry == 0 && c->bufchain) { - ssh2_channel_add_bufchain(c, buf, buflen); + ssh2_channel_add_bufchain(pvar, c, buf, buflen); return; } @@ -3448,7 +3470,7 @@ if ((unsigned int)buflen > c->remote_window) { unsigned int offset = 0; // \x91\x97\x82\xEA\x82Ȃ\xA2\x83f\x81[\x83^\x82͂\xA2\x82\xC1\x82\xBD\x82\xF1\x95ۑ\xB6\x82\xB5\x82Ă\xA8\x82\xAD - ssh2_channel_add_bufchain(c, buf + offset, buflen - offset); + ssh2_channel_add_bufchain(pvar, c, buf + offset, buflen - offset); buflen = offset; return; } Modified: trunk/ttssh2/ttxssh/ssh.h =================================================================== --- trunk/ttssh2/ttxssh/ssh.h 2019-06-14 23:40:58 UTC (rev 7770) +++ trunk/ttssh2/ttxssh/ssh.h 2019-06-15 06:42:23 UTC (rev 7771) @@ -881,6 +881,8 @@ enum channel_type type; int local_num; bufchain_t *bufchain; + unsigned long bufchain_amount; + BOOL bufchain_recv_suspended; scp_t scp; buffer_t *agent_msg; int agent_request_len; @@ -892,6 +894,7 @@ unsigned char *begin_send_packet(PTInstVar pvar, int type, int len); void finish_send_packet_special(PTInstVar pvar, int skip_compress); void SSH2_send_channel_data(PTInstVar pvar, Channel_t *c, unsigned char *buf, unsigned int buflen, int retry); +Channel_t* ssh2_local_channel_lookup(int local_num); #define finish_send_packet(pvar) finish_send_packet_special((pvar), 0) #define get_payload_uint32(pvar, offset) get_uint32_MSBfirst((pvar)->ssh_state.payload + (offset))