Your message dated Sat, 23 Mar 2019 15:37:23 +0000
with message-id <20190323153723.ga28...@powdarrmonkey.net>
and subject line Re: Bug#924904: unblock: putty/0.70-6
has caused the Debian Bug report #924904,
regarding unblock: putty/0.70-6
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
924904: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=924904
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock

The EU recently funded a bug bounty for PuTTY, and PuTTY 0.71 was
released over the weekend including a large number of security fixes
many of which were found by that.  Since this is too late for buster,
the upstream maintainer kindly sent me a backported patch series which
he recommended that we apply to 0.70, and I uploaded that to unstable
yesterday.  I think we should have this in buster, so please unblock.

(When I last asked, no CVEs had been allocated for any of this yet.)

unblock putty/0.70-6

Thanks,

-- 
Colin Watson                                       [cjwat...@debian.org]
diff -Nru putty-0.70/debian/.git-dpm putty-0.70/debian/.git-dpm
--- putty-0.70/debian/.git-dpm  2018-10-28 17:18:52.000000000 +0000
+++ putty-0.70/debian/.git-dpm  2019-03-17 09:36:53.000000000 +0000
@@ -1,6 +1,6 @@
 # see git-dpm(1) from git-dpm package
-694018afd4da9c7e00c7247c275e44b3aab49d4b
-694018afd4da9c7e00c7247c275e44b3aab49d4b
+1ebfc3bc04d0bbde174da1999a922b491a0e90dd
+1ebfc3bc04d0bbde174da1999a922b491a0e90dd
 8d3b8df5deee84238c92dfa4b4c4e3a787d73b64
 8d3b8df5deee84238c92dfa4b4c4e3a787d73b64
 putty_0.70.orig.tar.gz
diff -Nru putty-0.70/debian/changelog putty-0.70/debian/changelog
--- putty-0.70/debian/changelog 2018-10-28 18:07:45.000000000 +0000
+++ putty-0.70/debian/changelog 2019-03-17 09:37:02.000000000 +0000
@@ -1,3 +1,22 @@
+putty (0.70-6) unstable; urgency=high
+
+  * Apply security patch series from upstream:
+    - New facility for removing pending toplevel callbacks.
+    - Fix one-byte buffer overrun in random_add_noise().
+    - uxnet: clean up callbacks when closing a NetSocket.
+    - sk_tcp_close: fix memory leak of output bufchain.
+    - Fix handling of bad RSA key with n=p=q=0.
+    - Sanity-check the 'Public-Lines' field in ppk files.
+    - Introduce an enum of the uxsel / select_result flags.
+    - Switch to using poll(2) in place of select(2).
+    - RSA kex: enforce the minimum key length.
+    - Fix crash on ESC#6 + combining chars + GTK + odd-width terminal.
+    - Limit the number of combining chars per terminal cell.
+    - minibidi: fix read past end of line in rule W5.
+    - Fix crash printing a width-2 char in a width-1 terminal.
+
+ -- Colin Watson <cjwat...@debian.org>  Sun, 17 Mar 2019 09:37:02 +0000
+
 putty (0.70-5) unstable; urgency=medium
 
   [ Colin Watson ]
diff -Nru putty-0.70/debian/patches/fix-bad-rsa-key-handling.patch 
putty-0.70/debian/patches/fix-bad-rsa-key-handling.patch
--- putty-0.70/debian/patches/fix-bad-rsa-key-handling.patch    1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/fix-bad-rsa-key-handling.patch    2019-03-17 
09:36:52.000000000 +0000
@@ -0,0 +1,48 @@
+From 475366539d4bf768567b635782c577cdfde40026 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Wed, 6 Feb 2019 21:09:29 +0000
+Subject: Fix handling of bad RSA key with n=p=q=0.
+
+In this situation, rsa_verify won't notice anything wrong until it
+gets to the point where decbn() tries to subtract 1 from p, and
+underruns the Bignum buffer.
+
+Just in case some other attack vector reaches that same problem point,
+I've also put a protective assertion in decbn() itself just before the
+memory overwrite would have happened.
+
+Last-Update: 2019-03-16
+
+Patch-Name: fix-bad-rsa-key-handling.patch
+---
+ sshbn.c  | 1 +
+ sshrsa.c | 4 ++++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/sshbn.c b/sshbn.c
+index 6768204b..b21797f0 100644
+--- a/sshbn.c
++++ b/sshbn.c
+@@ -1400,6 +1400,7 @@ void decbn(Bignum bn)
+     int i = 1;
+     while (i < (int)bn[0] && bn[i] == 0)
+       bn[i++] = BIGNUM_INT_MASK;
++    assert(i < (int)bn[0]);
+     bn[i]--;
+ }
+ 
+diff --git a/sshrsa.c b/sshrsa.c
+index e565a64a..1dbf16bf 100644
+--- a/sshrsa.c
++++ b/sshrsa.c
+@@ -411,6 +411,10 @@ int rsa_verify(struct RSAKey *key)
+     Bignum n, ed, pm1, qm1;
+     int cmp;
+ 
++    /* n cannot be zero. */
++    if (!bignum_cmp(key->modulus, Zero))
++        return 0;
++
+     /* n must equal pq. */
+     n = bigmul(key->p, key->q);
+     cmp = bignum_cmp(n, key->modulus);
diff -Nru putty-0.70/debian/patches/fix-double-width-crash.patch 
putty-0.70/debian/patches/fix-double-width-crash.patch
--- putty-0.70/debian/patches/fix-double-width-crash.patch      1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/fix-double-width-crash.patch      2019-03-17 
09:36:53.000000000 +0000
@@ -0,0 +1,46 @@
+From 1ebfc3bc04d0bbde174da1999a922b491a0e90dd Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Thu, 14 Mar 2019 18:13:01 +0000
+Subject: Fix crash printing a width-2 char in a width-1 terminal.
+
+If the terminal is one column wide, it's not possible to print a
+double-width CJK character at all - it won't fit. Replace it with
+U+FFFD to indicate that impossibility.
+
+The previous behaviour was to notice that we're in the rightmost
+column of the terminal, and invoke the LATTR_WRAPPED2 special case to
+wrap to the leftmost column on the next line. But in a width-1
+terminal, the rightmost column _is_ the leftmost column, so this would
+leave us no better off, and we would have fallen through into the next
+case while in exactly the situation we'd tried to rule out.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=03777723e553024e94d8bfcf182f3a2e92ffb914
+Last-Update: 2019-03-16
+
+Patch-Name: fix-double-width-crash.patch
+---
+ terminal.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/terminal.c b/terminal.c
+index 8f1ef075..fd725d4b 100644
+--- a/terminal.c
++++ b/terminal.c
+@@ -3207,6 +3207,17 @@ static void term_out(Terminal *term)
+                       logtraffic(term->logctx, (unsigned char) c,
+                                  LGTYP_ASCII);
+ 
++                    /*
++                     * Preliminary check: if the terminal is only one
++                     * character cell wide, then we cannot display any
++                     * double-width character at all. Substitute
++                     * single-width REPLACEMENT CHARACTER instead.
++                     */
++                    if (width == 2 && term->cols < 2) {
++                        width = 1;
++                        c = 0xFFFD;
++                    }
++
+                   switch (width) {
+                     case 2:
+                       /*
diff -Nru putty-0.70/debian/patches/fix-esc6-combining-chars-crash.patch 
putty-0.70/debian/patches/fix-esc6-combining-chars-crash.patch
--- putty-0.70/debian/patches/fix-esc6-combining-chars-crash.patch      
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/fix-esc6-combining-chars-crash.patch      
2019-03-17 09:36:53.000000000 +0000
@@ -0,0 +1,45 @@
+From 30099b60d0c5a50c95c38080a568f3a2054d274a Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Thu, 14 Feb 2019 18:05:22 +0000
+Subject: Fix crash on ESC#6 + combining chars + GTK + odd-width terminal.
+
+When we're displaying double-width text as a result of the VT100 ESC#6
+escape sequence or its friends, and the terminal width is an odd
+number of columns, we divide by 2 the number of characters we'll even
+try to display, and round _down_: if there's a rightmost odd column,
+it stays blank, and doesn't show the left half of a double-width char.
+
+In the GTK redraw function, that rounding-down can set the 'len'
+variable to zero. But when we're displaying a character with Unicode
+combining chars on top, that fails an assertion that len == 1, because
+at the top of the function we set it to 1.
+
+The fix is just to return early if len is reduced to zero by that
+rounding: if we're not displaying any characters, then we don't have
+to do anything at all.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=daf91ef8ae9780bb1dfb534afa79e4babb89ba26
+Last-Update: 2019-03-16
+
+Patch-Name: fix-esc6-combining-chars-crash.patch
+---
+ unix/gtkwin.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/unix/gtkwin.c b/unix/gtkwin.c
+index bd57dc2e..61683d68 100644
+--- a/unix/gtkwin.c
++++ b/unix/gtkwin.c
+@@ -3305,8 +3305,11 @@ void do_text_internal(Context ctx, int x, int y, 
wchar_t *text, int len,
+       x *= 2;
+       if (x >= inst->term->cols)
+           return;
+-      if (x + len*2*widefactor > inst->term->cols)
++      if (x + len*2*widefactor > inst->term->cols) {
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
++            if (len == 0)
++                return; /* rounded down half a double-width char to zero */
++        }
+       rlen = len * 2;
+     } else
+       rlen = len;
diff -Nru putty-0.70/debian/patches/limit-combining-chars-per-cell.patch 
putty-0.70/debian/patches/limit-combining-chars-per-cell.patch
--- putty-0.70/debian/patches/limit-combining-chars-per-cell.patch      
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/limit-combining-chars-per-cell.patch      
2019-03-17 09:36:53.000000000 +0000
@@ -0,0 +1,126 @@
+From 471b1daee41d015a14e288cd927d00a9db6801b5 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Fri, 1 Mar 2019 19:20:12 +0000
+Subject: Limit the number of combining chars per terminal cell.
+
+The previous unlimited system was nicely general, but unfortunately
+meant you could easily DoS a PuTTY-based terminal by sending a
+printing character followed by an endless stream of identical
+combining chars. (In fact, due to accidentally-quadratic linked list
+management, you'd DoS it by using up all the CPU even before you got
+the point of making it allocate all the RAM.)
+
+The new limit is chosen to be 32, more or less arbitrarily. Overlong
+sequences of combining characters are signalled by turning the whole
+character cell into U+FFFD REPLACEMENT CHARACTER.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=da1c8f15b1bc14c855f0027cf06ba7f1a9c36f3c
+Last-Update: 2019-03-16
+
+Patch-Name: limit-combining-chars-per-cell.patch
+---
+ terminal.c | 52 +++++++++++++++++++++++++++++++++++++++++++++-------
+ terminal.h | 15 +++++++++++++++
+ 2 files changed, 60 insertions(+), 7 deletions(-)
+
+diff --git a/terminal.c b/terminal.c
+index ba9dd617..8f1ef075 100644
+--- a/terminal.c
++++ b/terminal.c
+@@ -196,6 +196,8 @@ static void cc_check(termline *line)
+ }
+ #endif
+ 
++static void clear_cc(termline *line, int col);
++
+ /*
+  * Add a combining character to a character cell.
+  */
+@@ -206,7 +208,49 @@ static void add_cc(termline *line, int col, unsigned long 
chr)
+     assert(col >= 0 && col < line->cols);
+ 
+     /*
+-     * Start by extending the cols array if the free list is empty.
++     * Don't add combining characters at all to U+FFFD REPLACEMENT
++     * CHARACTER. (Partly it's a slightly incoherent idea in the first
++     * place; mostly, U+FFFD is what we generate if a cell already has
++     * too many ccs, in which case we want it to be a fixed point when
++     * further ccs are added.)
++     */
++    if (line->chars[col].chr == 0xFFFD)
++        return;
++
++    /*
++     * Walk the cc list of the cell in question to find its current
++     * end point.
++     */
++    size_t ncc = 0;
++    int origcol = col;
++    while (line->chars[col].cc_next) {
++      col += line->chars[col].cc_next;
++        if (++ncc >= CC_LIMIT) {
++            /*
++             * There are already too many combining characters in this
++             * character cell. Change strategy: throw out the entire
++             * chain and replace the main character with U+FFFD.
++             *
++             * (Rationale: extrapolating from UTR #36 section 3.6.2
++             * suggests the principle that it's better to substitute
++             * U+FFFD than to _ignore_ input completely. Also, if the
++             * user copies and pastes an overcombined character cell,
++             * this way it will clearly indicate that we haven't
++             * reproduced the writer's original intentions, instead of
++             * looking as if it was the _writer's_ fault that the 33rd
++             * cc is missing.)
++             *
++             * Per the code above, this will also prevent any further
++             * ccs from being added to this cell.
++             */
++            clear_cc(line, origcol);
++            line->chars[origcol].chr = 0xFFFD;
++            return;
++        }
++    }
++
++    /*
++     * Extend the cols array if the free list is empty.
+      */
+     if (!line->cc_free) {
+       int n = line->size;
+@@ -222,12 +266,6 @@ static void add_cc(termline *line, int col, unsigned long 
chr)
+       }
+     }
+ 
+-    /*
+-     * Now walk the cc list of the cell in question.
+-     */
+-    while (line->chars[col].cc_next)
+-      col += line->chars[col].cc_next;
+-
+     /*
+      * `col' now points at the last cc currently in this cell; so
+      * we simply add another one.
+diff --git a/terminal.h b/terminal.h
+index 2ed9e6ef..38d154c6 100644
+--- a/terminal.h
++++ b/terminal.h
+@@ -327,4 +327,19 @@ struct terminal_tag {
+ 
+ #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
+ 
++/*
++ * Maximum number of combining characters we're willing to store in a
++ * character cell. Our linked-list data representation permits an
++ * unlimited number of these in principle, but if we allowed that in
++ * practice then it would be an easy DoS to just squirt a squillion
++ * identical combining characters to someone's terminal and cause
++ * their PuTTY or pterm to consume lots of memory and CPU pointlessly.
++ *
++ * The precise figure of 32 is more or less arbitrary, but one point
++ * supporting it is UAX #15's comment that 30 combining characters is
++ * "significantly beyond what is required for any linguistic or
++ * technical usage".
++ */
++#define CC_LIMIT 32
++
+ #endif
diff -Nru putty-0.70/debian/patches/minibidi-fix-read-past-end-of-line.patch 
putty-0.70/debian/patches/minibidi-fix-read-past-end-of-line.patch
--- putty-0.70/debian/patches/minibidi-fix-read-past-end-of-line.patch  
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/minibidi-fix-read-past-end-of-line.patch  
2019-03-17 09:36:53.000000000 +0000
@@ -0,0 +1,33 @@
+From 086e75a1cefd2c0727948fd2940ecda4338c11ce Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Sat, 9 Feb 2019 14:12:16 +0000
+Subject: minibidi: fix read past end of line in rule W5.
+
+The check for a sequence of ET with an EN after it could potentially
+skip ETs all the way up to the end of the buffer and then look for an
+EN in the following nonexistent array element. Now it only skips ETs
+up to count-1, in the same style as the similar check in rule N1.
+
+Change-Id: Ifdbae494a22d1b96bf49ae1bcae0efb901565f45
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=03492ab59369dc8347d8eb8693da548b5e27cf0b
+Last-Update: 2019-03-16
+
+Patch-Name: minibidi-fix-read-past-end-of-line.patch
+---
+ minibidi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/minibidi.c b/minibidi.c
+index 2bdf4deb..47ca558d 100644
+--- a/minibidi.c
++++ b/minibidi.c
+@@ -1413,7 +1413,7 @@ int do_bidi(bidi_char *line, int count)
+                 continue;
+             } else if (i < count-1 && types[i+1] == ET) {
+                 j=i;
+-                while (j <count && types[j] == ET) {
++                while (j < count-1 && types[j] == ET) {
+                     j++;
+                 }
+                 if (types[j] == EN)
diff -Nru putty-0.70/debian/patches/random-add-noise-overrun.patch 
putty-0.70/debian/patches/random-add-noise-overrun.patch
--- putty-0.70/debian/patches/random-add-noise-overrun.patch    1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/random-add-noise-overrun.patch    2019-03-17 
09:36:52.000000000 +0000
@@ -0,0 +1,69 @@
+From ac3bf240f22558a1a3ed5523fbfbb240c7637c74 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Tue, 29 Jan 2019 21:07:41 +0000
+Subject: Fix one-byte buffer overrun in random_add_noise().
+
+The variable 'poolpos' is incremented by two functions: random_byte(),
+which reads from the pool, and random_add_noise(), which writes to it.
+Both of them must handle the case where poolpos reaches its upper
+limit POOLSIZE, wrap poolpos round to the bottom, and trigger a
+random_stir().
+
+Unfortunately, random_byte checks that poolpos < POOLSIZE _before_ the
+read, so it may leave poolpos==POOLSIZE on exit. And random_add_noise
+does it the other way round - it assumes it's safe to write the first
+byte _before_ doing the bounds check. So if exactly the right number
+of random_byte calls happen before the next add_noise, then a byte of
+entropy can be XORed into the thing just beyond the end of pool[].
+
+What _is_ beyond that point is the LSB of poolpos itself! Since
+POOLSIZE is not a multiple of 256, this means that poolpos could be
+made larger or smaller by this overwrite. If it's made larger, that's
+the safe case - the subsequent bounds check will still fail, and then
+poolpos will be reset anyway. (And this is also what will happen in
+the very likely case that poolpos was cached in a register during that
+part of the function.)
+
+The dangerous case is if poolpos is made _smaller_ by that overwrite.
+In that situation, the effect will be to rewind the position in the
+random pool, delaying a necessary random_stir() and causing
+already-output random numbers to either be reused, or to be reused
+combined with unhashed input noise. In the latter case, it's just
+conceivable that an attacker who was somehow controlling at least one
+entropy source might be able to manipulate the RNG output on demand.
+
+Last-Update: 2019-03-16
+
+Patch-Name: random-add-noise-overrun.patch
+---
+ sshrand.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/sshrand.c b/sshrand.c
+index 31b1739e..05adbfa0 100644
+--- a/sshrand.c
++++ b/sshrand.c
+@@ -234,17 +234,20 @@ void random_add_noise(void *noise, int length)
+      * sources then we would be throwing away valuable stuff.
+      */
+     while (length >= (HASHINPUT - pool.incomingpos)) {
++        int need_stir = FALSE;
+       memcpy(pool.incomingb + pool.incomingpos, p,
+              HASHINPUT - pool.incomingpos);
+       p += HASHINPUT - pool.incomingpos;
+       length -= HASHINPUT - pool.incomingpos;
+       SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb);
+       for (i = 0; i < HASHSIZE; i++) {
+-          pool.pool[pool.poolpos++] ^= pool.incoming[i];
+-          if (pool.poolpos >= POOLSIZE)
++          if (pool.poolpos >= POOLSIZE) {
+               pool.poolpos = 0;
++                need_stir = TRUE;
++            }
++          pool.pool[pool.poolpos++] ^= pool.incoming[i];
+       }
+-      if (pool.poolpos < HASHSIZE)
++      if (need_stir)
+           random_stir();
+ 
+       pool.incomingpos = 0;
diff -Nru putty-0.70/debian/patches/remove-pending-toplevel-callbacks.patch 
putty-0.70/debian/patches/remove-pending-toplevel-callbacks.patch
--- putty-0.70/debian/patches/remove-pending-toplevel-callbacks.patch   
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/remove-pending-toplevel-callbacks.patch   
2019-03-17 09:36:52.000000000 +0000
@@ -0,0 +1,140 @@
+From 56d59111aab5be734e0467c4a189780013788709 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Sat, 25 Nov 2017 17:17:21 +0000
+Subject: New facility for removing pending toplevel callbacks.
+
+This is used when you're about to destroy an object that is
+(potentially) the context parameter for some still-pending toplevel
+callback. It causes callbacks.c to go through its pending list and
+delete any callback records referring to that context parameter, so
+that when you destroy the object those callbacks aren't still waiting
+to cause stale-pointer dereferences.
+
+(cherry picked from commit afa9734b7de6656fd8db0b077f00f39d8218a4c6)
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=afa9734b7de6656fd8db0b077f00f39d8218a4c6
+Last-Update: 2019-03-16
+
+Patch-Name: remove-pending-toplevel-callbacks.patch
+---
+ callback.c | 65 +++++++++++++++++++++++++++++++++++++++++-------------
+ putty.h    |  1 +
+ 2 files changed, 51 insertions(+), 15 deletions(-)
+
+diff --git a/callback.c b/callback.c
+index c70dc53f..84ea0c80 100644
+--- a/callback.c
++++ b/callback.c
+@@ -14,7 +14,7 @@ struct callback {
+     void *ctx;
+ };
+ 
+-struct callback *cbhead = NULL, *cbtail = NULL;
++struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
+ 
+ toplevel_callback_notify_fn_t notify_frontend = NULL;
+ void *frontend = NULL;
+@@ -26,6 +26,30 @@ void 
request_callback_notifications(toplevel_callback_notify_fn_t fn,
+     frontend = fr;
+ }
+ 
++void delete_callbacks_for_context(void *ctx)
++{
++    struct callback *newhead, *newtail;
++
++    newhead = newtail = NULL;
++    while (cbhead) {
++        struct callback *cb = cbhead;
++        cbhead = cbhead->next;
++        if (cb->ctx == ctx) {
++            sfree(cb);
++        } else {
++            if (!newhead)
++                newhead = cb;
++            else
++                newtail->next = cb;
++
++            newtail = cb;
++        }
++    }
++
++    cbhead = newhead;
++    cbtail = newtail;
++}
++
+ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
+ {
+     struct callback *cb;
+@@ -34,10 +58,18 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, 
void *ctx)
+     cb->fn = fn;
+     cb->ctx = ctx;
+ 
+-    /* If the front end has requested notification of pending
++    /*
++     * If the front end has requested notification of pending
+      * callbacks, and we didn't already have one queued, let it know
+-     * we do have one now. */
+-    if (notify_frontend && !cbhead)
++     * we do have one now.
++     *
++     * If cbcurr is non-NULL, i.e. we are actually in the middle of
++     * executing a callback right now, then we count that as the queue
++     * already having been non-empty. That saves the front end getting
++     * a constant stream of needless re-notifications if the last
++     * callback keeps re-scheduling itself.
++     */
++    if (notify_frontend && !cbhead && !cbcurr)
+         notify_frontend(frontend);
+ 
+     if (cbtail)
+@@ -51,24 +83,27 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, 
void *ctx)
+ void run_toplevel_callbacks(void)
+ {
+     if (cbhead) {
+-        struct callback *cb = cbhead;
+         /*
+-         * Careful ordering here. We call the function _before_
+-         * advancing cbhead (though, of course, we must free cb
+-         * _after_ advancing it). This means that if the very last
+-         * callback schedules another callback, cbhead does not become
+-         * NULL at any point, and so the frontend notification
+-         * function won't be needlessly pestered.
++         * Transfer the head callback into cbcurr to indicate that
++         * it's being executed. Then operations which transform the
++         * queue, like delete_callbacks_for_context, can proceed as if
++         * it's not there.
+          */
+-        cb->fn(cb->ctx);
+-        cbhead = cb->next;
+-        sfree(cb);
++        cbcurr = cbhead;
++        cbhead = cbhead->next;
+         if (!cbhead)
+             cbtail = NULL;
++
++        /*
++         * Now run the callback, and then clear it out of cbcurr.
++         */
++        cbcurr->fn(cbcurr->ctx);
++        sfree(cbcurr);
++        cbcurr = NULL;
+     }
+ }
+ 
+ int toplevel_callback_pending(void)
+ {
+-    return cbhead != NULL;
++    return cbcurr != NULL || cbhead != NULL;
+ }
+diff --git a/putty.h b/putty.h
+index fd2d0250..28fb59a0 100644
+--- a/putty.h
++++ b/putty.h
+@@ -1507,6 +1507,7 @@ typedef void (*toplevel_callback_fn_t)(void *ctx);
+ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx);
+ void run_toplevel_callbacks(void);
+ int toplevel_callback_pending(void);
++void delete_callbacks_for_context(void *ctx);
+ 
+ typedef void (*toplevel_callback_notify_fn_t)(void *frontend);
+ void request_callback_notifications(toplevel_callback_notify_fn_t notify,
diff -Nru putty-0.70/debian/patches/rsa-kex-enforce-minimum-key-length.patch 
putty-0.70/debian/patches/rsa-kex-enforce-minimum-key-length.patch
--- putty-0.70/debian/patches/rsa-kex-enforce-minimum-key-length.patch  
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/rsa-kex-enforce-minimum-key-length.patch  
2019-03-17 09:36:53.000000000 +0000
@@ -0,0 +1,77 @@
+From cb90098659f9bdd80b9e488dc7f3e291ac0da36d Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Thu, 7 Feb 2019 20:04:17 +0000
+Subject: RSA kex: enforce the minimum key length.
+
+I completely forgot to check that the server had actually sent a key
+of at least MINKLEN bits, as RFC 4432 clearly says that it MUST.
+Without this restriction, not only can a server trick the client into
+using a shared secret with inadequate entropy, but it can send a key
+so short that the client attempts to generate a secret integer of
+negative length, with integer-overflowing results.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=d82854999516046122501b2e145099740ed0284f
+Last-Update: 2019-03-16
+
+Patch-Name: rsa-kex-enforce-minimum-key-length.patch
+---
+ ssh.c    | 12 ++++++++++++
+ ssh.h    |  2 ++
+ sshrsa.c |  4 ++--
+ 3 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/ssh.c b/ssh.c
+index 1d80e919..1edecfe1 100644
+--- a/ssh.c
++++ b/ssh.c
+@@ -7251,7 +7251,19 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, 
int inlen,
+          */
+         {
+             int klen = ssh_rsakex_klen(s->rsakey);
++
++            int minklen = (ssh->kex == &ssh_rsa_kex_sha1 ? 1024 : 2048);
++            if (klen < minklen) {
++                sfree(s->rsakeydata);
++                bombout(("Server sent %d-bit RSA key, "
++                         "less than the minimum size %d for %s "
++                         "key exchange", klen, minklen, ssh->kex->name));
++                crStopV;
++            }
++
+             int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
++            assert(nbits > 0);
++
+             int i, byte = 0;
+             unsigned char *kstr1, *kstr2, *outstr;
+             int kstr1len, kstr2len, outstrlen;
+diff --git a/ssh.h b/ssh.h
+index 371de837..3cafc7f9 100644
+--- a/ssh.h
++++ b/ssh.h
+@@ -463,6 +463,8 @@ extern const struct ssh_kexes ssh_diffiehellman_group1;
+ extern const struct ssh_kexes ssh_diffiehellman_group14;
+ extern const struct ssh_kexes ssh_diffiehellman_gex;
+ extern const struct ssh_kexes ssh_rsa_kex;
++extern const struct ssh_kex ssh_rsa_kex_sha1;
++extern const struct ssh_kex ssh_rsa_kex_sha256;
+ extern const struct ssh_kexes ssh_ecdh_kex;
+ extern const struct ssh_signkey ssh_dss;
+ extern const struct ssh_signkey ssh_rsa;
+diff --git a/sshrsa.c b/sshrsa.c
+index 1dbf16bf..e066fd25 100644
+--- a/sshrsa.c
++++ b/sshrsa.c
+@@ -1063,11 +1063,11 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, 
unsigned char *in, int inlen,
+      */
+ }
+ 
+-static const struct ssh_kex ssh_rsa_kex_sha1 = {
++const struct ssh_kex ssh_rsa_kex_sha1 = {
+     "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, NULL,
+ };
+ 
+-static const struct ssh_kex ssh_rsa_kex_sha256 = {
++const struct ssh_kex ssh_rsa_kex_sha256 = {
+     "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, NULL,
+ };
+ 
diff -Nru putty-0.70/debian/patches/sanity-check-public-lines.patch 
putty-0.70/debian/patches/sanity-check-public-lines.patch
--- putty-0.70/debian/patches/sanity-check-public-lines.patch   1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/sanity-check-public-lines.patch   2019-03-17 
09:36:53.000000000 +0000
@@ -0,0 +1,100 @@
+From 1134ca985ce1601410f0e21edbd4b1643051e8d1 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Sat, 19 Jan 2019 07:40:07 +0000
+Subject: Sanity-check the 'Public-Lines' field in ppk files.
+
+If it's too large, memory allocation can fail, or worse,
+under-allocate due to integer overflow.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=63a58759b5c0c11183726767a095f3a154b0f131
+Last-Update: 2019-03-16
+
+Patch-Name: sanity-check-public-lines.patch
+---
+ sshpubk.c | 35 ++++++++++++++++++++++++++++++++---
+ 1 file changed, 32 insertions(+), 3 deletions(-)
+
+diff --git a/sshpubk.c b/sshpubk.c
+index 1a27c313..acf2292c 100644
+--- a/sshpubk.c
++++ b/sshpubk.c
+@@ -23,6 +23,18 @@
+ 
+ static int key_type_fp(FILE *fp);
+ 
++/*
++ * Fairly arbitrary size limit on any public or private key blob.
++ * Chosen to match AGENT_MAX_MSGLEN, on the basis that any key too
++ * large to transfer over the ssh-agent protocol is probably too large
++ * to be useful in general.
++ *
++ * MAX_KEY_BLOB_LINES is the corresponding limit on the Public-Lines
++ * or Private-Lines header field in a key file.
++ */
++#define MAX_KEY_BLOB_SIZE 262144
++#define MAX_KEY_BLOB_LINES (MAX_KEY_BLOB_SIZE / 48)
++
+ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
+                          char **commentptr, const char *passphrase,
+                          const char **error)
+@@ -575,6 +587,7 @@ static unsigned char *read_blob(FILE * fp, int nlines, int 
*bloblen)
+     int i, j, k;
+ 
+     /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */
++    assert(nlines < MAX_KEY_BLOB_LINES);
+     blob = snewn(48 * nlines, unsigned char);
+     len = 0;
+     for (i = 0; i < nlines; i++) {
+@@ -634,6 +647,16 @@ const struct ssh_signkey *find_pubkey_alg(const char 
*name)
+     return find_pubkey_alg_len(strlen(name), name);
+ }
+ 
++static int userkey_parse_line_counter(const char *text)
++{
++    char *endptr;
++    unsigned long ul = strtoul(text, &endptr, 10);
++    if (*text && !*endptr && ul < MAX_KEY_BLOB_LINES)
++        return ul;
++    else
++        return -1;
++}
++
+ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
+                                      const char *passphrase,
+                                        const char **errorstr)
+@@ -714,8 +737,10 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename 
*filename,
+       goto error;
+     if ((b = read_body(fp)) == NULL)
+       goto error;
+-    i = atoi(b);
++    i = userkey_parse_line_counter(b);
+     sfree(b);
++    if (i < 0)
++        goto error;
+     if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
+       goto error;
+ 
+@@ -724,8 +749,10 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename 
*filename,
+       goto error;
+     if ((b = read_body(fp)) == NULL)
+       goto error;
+-    i = atoi(b);
++    i = userkey_parse_line_counter(b);
+     sfree(b);
++    if (i < 0)
++        goto error;
+     if ((private_blob = read_blob(fp, i, &private_blob_len)) == NULL)
+       goto error;
+ 
+@@ -1186,8 +1213,10 @@ unsigned char *ssh2_userkey_loadpub(const Filename 
*filename, char **algorithm,
+       goto error;
+     if ((b = read_body(fp)) == NULL)
+       goto error;
+-    i = atoi(b);
++    i = userkey_parse_line_counter(b);
+     sfree(b);
++    if (i < 0)
++        goto error;
+     if ((public_blob = read_blob(fp, i, &public_blob_len)) == NULL)
+       goto error;
+ 
diff -Nru putty-0.70/debian/patches/series putty-0.70/debian/patches/series
--- putty-0.70/debian/patches/series    2018-10-28 17:18:52.000000000 +0000
+++ putty-0.70/debian/patches/series    2019-03-17 09:36:52.000000000 +0000
@@ -5,3 +5,16 @@
 pscp-fixed-size-buffer.patch
 cmdgen-fixed-size-buffer.patch
 gtk-focus.patch
+remove-pending-toplevel-callbacks.patch
+random-add-noise-overrun.patch
+uxnet-clean-up-callbacks.patch
+sk_tcp_close-fix-memory-leak.patch
+fix-bad-rsa-key-handling.patch
+sanity-check-public-lines.patch
+uxsel-enum.patch
+switch-to-poll.patch
+rsa-kex-enforce-minimum-key-length.patch
+fix-esc6-combining-chars-crash.patch
+limit-combining-chars-per-cell.patch
+minibidi-fix-read-past-end-of-line.patch
+fix-double-width-crash.patch
diff -Nru putty-0.70/debian/patches/sk_tcp_close-fix-memory-leak.patch 
putty-0.70/debian/patches/sk_tcp_close-fix-memory-leak.patch
--- putty-0.70/debian/patches/sk_tcp_close-fix-memory-leak.patch        
1970-01-01 01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/sk_tcp_close-fix-memory-leak.patch        
2019-03-17 09:36:52.000000000 +0000
@@ -0,0 +1,44 @@
+From 5e4227e8e306fea03b7f6a3aa3d1b664af8c2178 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Tue, 29 Jan 2019 20:13:47 +0000
+Subject: sk_tcp_close: fix memory leak of output bufchain.
+
+If there was still pending output data on a NetSocket's output_data
+bufchain when it was closed, then we wouldn't have freed it, on either
+Unix or Windows.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=0212b9e5e5cb55ecaf678a65c2ef0d8d968c8a26
+Last-Update: 2019-03-16
+
+Patch-Name: sk_tcp_close-fix-memory-leak.patch
+---
+ unix/uxnet.c     | 2 ++
+ windows/winnet.c | 2 ++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/unix/uxnet.c b/unix/uxnet.c
+index 6549eca5..35a90ac7 100644
+--- a/unix/uxnet.c
++++ b/unix/uxnet.c
+@@ -1023,6 +1023,8 @@ static void sk_tcp_close(Socket sock)
+     if (s->child)
+         sk_tcp_close((Socket)s->child);
+ 
++    bufchain_clear(&s->output_data);
++
+     uxsel_del(s->s);
+     del234(sktree, s);
+     close(s->s);
+diff --git a/windows/winnet.c b/windows/winnet.c
+index 86f7735f..3c02c3f8 100644
+--- a/windows/winnet.c
++++ b/windows/winnet.c
+@@ -1464,6 +1464,8 @@ static void sk_tcp_close(Socket sock)
+     if (s->child)
+       sk_tcp_close((Socket)s->child);
+ 
++    bufchain_clear(&s->output_data);
++
+     del234(sktree, s);
+     do_select(s->s, 0);
+     p_closesocket(s->s);
diff -Nru putty-0.70/debian/patches/switch-to-poll.patch 
putty-0.70/debian/patches/switch-to-poll.patch
--- putty-0.70/debian/patches/switch-to-poll.patch      1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/switch-to-poll.patch      2019-03-17 
09:36:53.000000000 +0000
@@ -0,0 +1,842 @@
+From 685388367abb55cd685fbae4e2d7c10fda6c1958 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Thu, 7 Feb 2019 18:21:06 +0000
+Subject: Switch to using poll(2) in place of select(2).
+
+I've always thought poll was more hassle to set up, because if you
+want to reuse part of your pollfds list between calls then you have to
+index every fd by its position in the list as well as the fd number
+itself, which gives you twice as many indices to keep track of than if
+the fd is always its own key.
+
+But the problem is that select is fundamentally limited to the range
+of fds that can fit in an fd_set, which is not the range of fds that
+can _exist_, so I've had a change of heart and now have to go with
+poll.
+
+For the moment, I've surrounded it with a 'pollwrapper' structure that
+lets me treat it more or less like select, containing a tree234 that
+maps each fd to its location in the list, and also translating between
+the simple select r/w/x classification and the richer poll flags.
+That's let me do the migration with minimal disruption to the call
+sites.
+
+In future perhaps I can start using poll more directly, and/or using
+the richer flag system (though the latter might be fiddly because of
+sometimes being constrained to use the glib event loop). But this will
+do for now.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=5c926d9ea4a9e0a0a2384f06c7583648cdff3ed6
+Last-Update: 2019-03-16
+
+Patch-Name: switch-to-poll.patch
+---
+ Recipe         |   4 +-
+ cmdline.c      |   2 +-
+ configure.ac   |   2 +-
+ putty.h        |   4 +-
+ unix/unix.h    |  19 +++++++
+ unix/uxcons.c  |  18 +++----
+ unix/uxnet.c   |   2 +-
+ unix/uxpgnt.c  |  56 +++++++++-----------
+ unix/uxplink.c |  70 +++++++++++-------------
+ unix/uxpoll.c  | 141 +++++++++++++++++++++++++++++++++++++++++++++++++
+ unix/uxsel.c   |   4 +-
+ unix/uxsftp.c  |  57 +++++++++-----------
+ 12 files changed, 260 insertions(+), 119 deletions(-)
+ create mode 100644 unix/uxpoll.c
+
+diff --git a/Recipe b/Recipe
+index f5458122..dfbf3f04 100644
+--- a/Recipe
++++ b/Recipe
+@@ -263,7 +263,7 @@ SFTP     = sftp int64 logging
+ MISC     = timing callback misc version settings tree234 proxy conf be_misc
+ WINMISC  = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
+          + wintime winhsock errsock winsecur winucs miscucs
+-UXMISC   = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time
++UXMISC   = MISC uxstore uxsel uxpoll uxnet uxpeer cmdline uxmisc uxproxy time
+ 
+ # import.c and dependencies, for PuTTYgen-like utilities that have to
+ # load foreign key files.
+@@ -330,7 +330,7 @@ plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL 
logging UXMISC uxsignal
+ PUTTYGEN_UNIX = sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+          + sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
+          + sshpubk sshaes sshsh256 sshsh512 IMPORT puttygen.res time tree234
+-         + uxgen notiming conf sshecc sshecdsag uxnogtk
++         + uxgen notiming conf sshecc sshecdsag uxnogtk uxpoll
+ puttygen : [U] cmdgen PUTTYGEN_UNIX
+ cgtest   : [UT] cgtest PUTTYGEN_UNIX
+ 
+diff --git a/cmdline.c b/cmdline.c
+index f288ed62..83b55717 100644
+--- a/cmdline.c
++++ b/cmdline.c
+@@ -121,7 +121,7 @@ int cmdline_get_passwd_input(prompts_t *p, const unsigned 
char *in, int inlen)
+  * transfer tools (psftp, pscp) can't do a great deal with protocol
+  * selections (ever tried running scp over telnet?) or with port
+  * forwarding (even if it wasn't a hideously bad idea, they don't
+- * have the select() infrastructure to make them work).
++ * have the select/poll infrastructure to make them work).
+  */
+ int cmdline_tooltype = 0;
+ 
+diff --git a/configure.ac b/configure.ac
+index 1b760920..ca0bbba9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -87,7 +87,7 @@ case "$gtk_version_desired" in
+   *) AC_ERROR([Invalid GTK version specified])
+ esac
+ 
+-AC_CHECK_HEADERS([utmpx.h sys/select.h],,,[
++AC_CHECK_HEADERS([utmpx.h],,,[
+ #include <sys/types.h>
+ #include <utmp.h>])
+ 
+diff --git a/putty.h b/putty.h
+index 28fb59a0..9c822744 100644
+--- a/putty.h
++++ b/putty.h
+@@ -1427,7 +1427,7 @@ char filename_char_sanitise(char c);   /* rewrite 
special pathname chars */
+  * The reason for this is that an OS's system clock might not agree
+  * exactly with the timing mechanisms it supplies to wait for a
+  * given interval. I'll illustrate this by the simple example of
+- * Unix Plink, which uses timeouts to select() in a way which for
++ * Unix Plink, which uses timeouts to poll() in a way which for
+  * these purposes can simply be considered to be a wait() function.
+  * Suppose, for the sake of argument, that this wait() function
+  * tends to return early by 1%. Then a possible sequence of actions
+@@ -1499,7 +1499,7 @@ unsigned long timing_last_clock(void);
+  * instead request notifications when a callback is available, so that
+  * it knows to ask its delegate event loop to do the same thing. Also,
+  * if a front end needs to know whether a callback is pending without
+- * actually running it (e.g. so as to put a zero timeout on a select()
++ * actually running it (e.g. so as to put a zero timeout on a poll()
+  * call) then it can call toplevel_callback_pending(), which will
+  * return true if at least one callback is in the queue.
+  */
+diff --git a/unix/unix.h b/unix/unix.h
+index 0bd012cb..af2f4a7c 100644
+--- a/unix/unix.h
++++ b/unix/unix.h
+@@ -264,4 +264,23 @@ int so_peercred(int fd, int *pid, int *uid, int *gid);
+ #define DEFAULT_GTK_FONT "server:fixed"
+ #endif
+ 
++/*
++ * uxpoll.c.
++ */
++typedef struct pollwrapper pollwrapper;
++pollwrapper *pollwrap_new(void);
++void pollwrap_free(pollwrapper *pw);
++void pollwrap_clear(pollwrapper *pw);
++void pollwrap_add_fd_events(pollwrapper *pw, int fd, int events);
++void pollwrap_add_fd_rwx(pollwrapper *pw, int fd, int rwx);
++int pollwrap_poll_instant(pollwrapper *pw);
++int pollwrap_poll_endless(pollwrapper *pw);
++int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds);
++int pollwrap_get_fd_events(pollwrapper *pw, int fd);
++int pollwrap_get_fd_rwx(pollwrapper *pw, int fd);
++static inline int pollwrap_check_fd_rwx(pollwrapper *pw, int fd, int rwx)
++{
++    return (pollwrap_get_fd_rwx(pw, fd) & rwx) != 0;
++}
++
+ #endif
+diff --git a/unix/uxcons.c b/unix/uxcons.c
+index 716f3fc5..6df81509 100644
+--- a/unix/uxcons.c
++++ b/unix/uxcons.c
+@@ -13,9 +13,6 @@
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/time.h>
+-#ifndef HAVE_NO_SYS_SELECT_H
+-#include <sys/select.h>
+-#endif
+ 
+ #include "putty.h"
+ #include "storage.h"
+@@ -82,12 +79,13 @@ void timer_change_notify(unsigned long next)
+ /*
+  * Wrapper around Unix read(2), suitable for use on a file descriptor
+  * that's been set into nonblocking mode. Handles EAGAIN/EWOULDBLOCK
+- * by means of doing a one-fd select and then trying again; all other
+- * errors (including errors from select) are returned to the caller.
++ * by means of doing a one-fd poll and then trying again; all other
++ * errors (including errors from poll) are returned to the caller.
+  */
+ static int block_and_read(int fd, void *buf, size_t len)
+ {
+     int ret;
++    pollwrapper *pw = pollwrap_new();
+ 
+     while ((ret = read(fd, buf, len)) < 0 && (
+ #ifdef EAGAIN
+@@ -98,18 +96,18 @@ static int block_and_read(int fd, void *buf, size_t len)
+ #endif
+                0)) {
+ 
+-        fd_set rfds;
+-        FD_ZERO(&rfds);
+-        FD_SET(fd, &rfds);
++        pollwrap_clear(pw);
++        pollwrap_add_fd_rwx(pw, fd, SELECT_R);
+         do {
+-            ret = select(fd+1, &rfds, NULL, NULL, NULL);
++            ret = pollwrap_poll_endless(pw);
+         } while (ret < 0 && errno == EINTR);
+         assert(ret != 0);
+         if (ret < 0)
+             return ret;
+-        assert(FD_ISSET(fd, &rfds));
++        assert(pollwrap_check_fd_rwx(pw, fd, SELECT_R));
+     }
+ 
++    pollwrap_free(pw);
+     return ret;
+ }
+ 
+diff --git a/unix/uxnet.c b/unix/uxnet.c
+index f2e35b38..ba6a1138 100644
+--- a/unix/uxnet.c
++++ b/unix/uxnet.c
+@@ -1401,7 +1401,7 @@ static void net_select_result(int fd, int event)
+       case SELECT_W:                   /* writable */
+       if (!s->connected) {
+           /*
+-           * select() reports a socket as _writable_ when an
++           * select/poll reports a socket as _writable_ when an
+            * asynchronous connect() attempt either completes or
+            * fails. So first we must find out which.
+            */
+diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
+index eba08632..37360a21 100644
+--- a/unix/uxpgnt.c
++++ b/unix/uxpgnt.c
+@@ -8,6 +8,7 @@
+ #include <assert.h>
+ #include <signal.h>
+ #include <ctype.h>
++#include <limits.h>
+ 
+ #include <sys/types.h>
+ #include <sys/wait.h>
+@@ -845,20 +846,17 @@ void run_agent(void)
+ 
+     now = GETTICKCOUNT();
+ 
++    pollwrapper *pw = pollwrap_new();
++
+     while (!time_to_die) {
+-      fd_set rset, wset, xset;
+-      int maxfd;
+       int rwx;
+       int ret;
+         unsigned long next;
+ 
+-      FD_ZERO(&rset);
+-      FD_ZERO(&wset);
+-      FD_ZERO(&xset);
+-      maxfd = 0;
++        pollwrap_clear(pw);
+ 
+         if (signalpipe[0] >= 0) {
+-            FD_SET_MAX(signalpipe[0], maxfd, rset);
++            pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R);
+         }
+ 
+       /* Count the currently active fds. */
+@@ -873,30 +871,21 @@ void run_agent(void)
+       }
+ 
+       /*
+-       * Add all currently open fds to the select sets, and store
+-       * them in fdlist as well.
++       * Add all currently open fds to pw, and store them in fdlist
++       * as well.
+        */
+       fdcount = 0;
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;
+            fd = next_fd(&fdstate, &rwx)) {
+           fdlist[fdcount++] = fd;
+-          if (rwx & 1)
+-              FD_SET_MAX(fd, maxfd, rset);
+-          if (rwx & 2)
+-              FD_SET_MAX(fd, maxfd, wset);
+-          if (rwx & 4)
+-              FD_SET_MAX(fd, maxfd, xset);
++            pollwrap_add_fd_rwx(pw, fd, rwx);
+       }
+ 
+         if (toplevel_callback_pending()) {
+-            struct timeval tv;
+-            tv.tv_sec = 0;
+-            tv.tv_usec = 0;
+-            ret = select(maxfd, &rset, &wset, &xset, &tv);
++            ret = pollwrap_poll_instant(pw);
+         } else if (run_timers(now, &next)) {
+             unsigned long then;
+             long ticks;
+-            struct timeval tv;
+ 
+             then = now;
+             now = GETTICKCOUNT();
+@@ -904,22 +893,27 @@ void run_agent(void)
+                 ticks = 0;
+             else
+                 ticks = next - now;
+-            tv.tv_sec = ticks / 1000;
+-            tv.tv_usec = ticks % 1000 * 1000;
+-            ret = select(maxfd, &rset, &wset, &xset, &tv);
+-            if (ret == 0)
++
++            int overflow = FALSE;
++            if (ticks > INT_MAX) {
++                ticks = INT_MAX;
++                overflow = TRUE;
++            }
++
++            ret = pollwrap_poll_timeout(pw, ticks);
++            if (ret == 0 && !overflow)
+                 now = next;
+             else
+                 now = GETTICKCOUNT();
+         } else {
+-            ret = select(maxfd, &rset, &wset, &xset, NULL);
++            ret = pollwrap_poll_endless(pw);
+         }
+ 
+         if (ret < 0 && errno == EINTR)
+             continue;
+ 
+       if (ret < 0) {
+-          perror("select");
++          perror("poll");
+           exit(1);
+       }
+ 
+@@ -939,20 +933,22 @@ void run_agent(void)
+ 
+       for (i = 0; i < fdcount; i++) {
+           fd = fdlist[i];
++            int rwx = pollwrap_get_fd_rwx(pw, fd);
+             /*
+              * We must process exceptional notifications before
+              * ordinary readability ones, or we may go straight
+              * past the urgent marker.
+              */
+-          if (FD_ISSET(fd, &xset))
++          if (rwx & SELECT_X)
+               select_result(fd, SELECT_X);
+-          if (FD_ISSET(fd, &rset))
++          if (rwx & SELECT_R)
+               select_result(fd, SELECT_R);
+-          if (FD_ISSET(fd, &wset))
++          if (rwx & SELECT_W)
+               select_result(fd, SELECT_W);
+       }
+ 
+-        if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
++        if (signalpipe[0] >= 0 &&
++            pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) {
+             char c[1];
+             if (read(signalpipe[0], c, 1) <= 0)
+                 /* ignore error */;
+diff --git a/unix/uxplink.c b/unix/uxplink.c
+index e7289bf2..521ed215 100644
+--- a/unix/uxplink.c
++++ b/unix/uxplink.c
+@@ -8,15 +8,13 @@
+ #include <assert.h>
+ #include <stdarg.h>
+ #include <signal.h>
++#include <limits.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <termios.h>
+ #include <pwd.h>
+ #include <sys/ioctl.h>
+ #include <sys/time.h>
+-#ifndef HAVE_NO_SYS_SELECT_H
+-#include <sys/select.h>
+-#endif
+ 
+ #define PUTTY_DO_GLOBALS             /* actually _define_ globals */
+ #include "putty.h"
+@@ -1038,36 +1036,33 @@ int main(int argc, char **argv)
+     sending = FALSE;
+     now = GETTICKCOUNT();
+ 
++    pollwrapper *pw = pollwrap_new();
++
+     while (1) {
+-      fd_set rset, wset, xset;
+-      int maxfd;
+       int rwx;
+       int ret;
+         unsigned long next;
+ 
+-      FD_ZERO(&rset);
+-      FD_ZERO(&wset);
+-      FD_ZERO(&xset);
+-      maxfd = 0;
++        pollwrap_clear(pw);
+ 
+-      FD_SET_MAX(signalpipe[0], maxfd, rset);
++      pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R);
+ 
+       if (!sending &&
+           back->connected(backhandle) &&
+           back->sendok(backhandle) &&
+           back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
+           /* If we're OK to send, then try to read from stdin. */
+-          FD_SET_MAX(STDIN_FILENO, maxfd, rset);
++            pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R);
+       }
+ 
+       if (bufchain_size(&stdout_data) > 0) {
+           /* If we have data for stdout, try to write to stdout. */
+-          FD_SET_MAX(STDOUT_FILENO, maxfd, wset);
++            pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W);
+       }
+ 
+       if (bufchain_size(&stderr_data) > 0) {
+           /* If we have data for stderr, try to write to stderr. */
+-          FD_SET_MAX(STDERR_FILENO, maxfd, wset);
++            pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W);
+       }
+ 
+       /* Count the currently active fds. */
+@@ -1082,31 +1077,22 @@ int main(int argc, char **argv)
+       }
+ 
+       /*
+-       * Add all currently open fds to the select sets, and store
+-       * them in fdlist as well.
++       * Add all currently open fds to pw, and store them in fdlist
++       * as well.
+        */
+       fdcount = 0;
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;
+            fd = next_fd(&fdstate, &rwx)) {
+           fdlist[fdcount++] = fd;
+-          if (rwx & 1)
+-              FD_SET_MAX(fd, maxfd, rset);
+-          if (rwx & 2)
+-              FD_SET_MAX(fd, maxfd, wset);
+-          if (rwx & 4)
+-              FD_SET_MAX(fd, maxfd, xset);
++            pollwrap_add_fd_rwx(pw, fd, rwx);
+       }
+ 
+         if (toplevel_callback_pending()) {
+-            struct timeval tv;
+-            tv.tv_sec = 0;
+-            tv.tv_usec = 0;
+-            ret = select(maxfd, &rset, &wset, &xset, &tv);
++            ret = pollwrap_poll_instant(pw);
+         } else if (run_timers(now, &next)) {
+             do {
+                 unsigned long then;
+                 long ticks;
+-                struct timeval tv;
+ 
+               then = now;
+               now = GETTICKCOUNT();
+@@ -1114,42 +1100,48 @@ int main(int argc, char **argv)
+                   ticks = 0;
+               else
+                   ticks = next - now;
+-              tv.tv_sec = ticks / 1000;
+-              tv.tv_usec = ticks % 1000 * 1000;
+-                ret = select(maxfd, &rset, &wset, &xset, &tv);
+-                if (ret == 0)
++
++                int overflow = FALSE;
++                if (ticks > INT_MAX) {
++                    ticks = INT_MAX;
++                    overflow = TRUE;
++                }
++
++                ret = pollwrap_poll_timeout(pw, ticks);
++                if (ret == 0 && !overflow)
+                     now = next;
+                 else
+                     now = GETTICKCOUNT();
+             } while (ret < 0 && errno == EINTR);
+         } else {
+-            ret = select(maxfd, &rset, &wset, &xset, NULL);
++            ret = pollwrap_poll_endless(pw);
+         }
+ 
+         if (ret < 0 && errno == EINTR)
+             continue;
+ 
+       if (ret < 0) {
+-          perror("select");
++          perror("poll");
+           exit(1);
+       }
+ 
+       for (i = 0; i < fdcount; i++) {
+           fd = fdlist[i];
++            int rwx = pollwrap_get_fd_rwx(pw, fd);
+             /*
+              * We must process exceptional notifications before
+              * ordinary readability ones, or we may go straight
+              * past the urgent marker.
+              */
+-          if (FD_ISSET(fd, &xset))
++          if (rwx & SELECT_X)
+               select_result(fd, SELECT_X);
+-          if (FD_ISSET(fd, &rset))
++          if (rwx & SELECT_R)
+               select_result(fd, SELECT_R);
+-          if (FD_ISSET(fd, &wset))
++          if (rwx & SELECT_W)
+               select_result(fd, SELECT_W);
+       }
+ 
+-      if (FD_ISSET(signalpipe[0], &rset)) {
++      if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) {
+           char c[1];
+           struct winsize size;
+           if (read(signalpipe[0], c, 1) <= 0)
+@@ -1159,7 +1151,7 @@ int main(int argc, char **argv)
+               back->size(backhandle, size.ws_col, size.ws_row);
+       }
+ 
+-      if (FD_ISSET(STDIN_FILENO, &rset)) {
++      if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) {
+           char buf[4096];
+           int ret;
+ 
+@@ -1180,11 +1172,11 @@ int main(int argc, char **argv)
+           }
+       }
+ 
+-      if (FD_ISSET(STDOUT_FILENO, &wset)) {
++      if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) {
+           back->unthrottle(backhandle, try_output(FALSE));
+       }
+ 
+-      if (FD_ISSET(STDERR_FILENO, &wset)) {
++      if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) {
+           back->unthrottle(backhandle, try_output(TRUE));
+       }
+ 
+diff --git a/unix/uxpoll.c b/unix/uxpoll.c
+new file mode 100644
+index 00000000..16ad0254
+--- /dev/null
++++ b/unix/uxpoll.c
+@@ -0,0 +1,141 @@
++#include <assert.h>
++
++#include <poll.h>
++
++#include "putty.h"
++#include "tree234.h"
++
++struct pollwrapper {
++    struct pollfd *fds;
++    size_t nfd, fdsize;
++    tree234 *fdtopos;
++};
++
++typedef struct pollwrap_fdtopos pollwrap_fdtopos;
++struct pollwrap_fdtopos {
++    int fd;
++    size_t pos;
++};
++
++static int pollwrap_fd_cmp(void *av, void *bv)
++{
++    pollwrap_fdtopos *a = (pollwrap_fdtopos *)av;
++    pollwrap_fdtopos *b = (pollwrap_fdtopos *)bv;
++    return a->fd < b->fd ? -1 : a->fd > b->fd ? +1 : 0;
++}
++
++pollwrapper *pollwrap_new(void)
++{
++    pollwrapper *pw = snew(pollwrapper);
++    pw->fdsize = 16;
++    pw->nfd = 0;
++    pw->fds = snewn(pw->fdsize, struct pollfd);
++    pw->fdtopos = newtree234(pollwrap_fd_cmp);
++    return pw;
++}
++
++void pollwrap_free(pollwrapper *pw)
++{
++    pollwrap_clear(pw);
++    freetree234(pw->fdtopos);
++    sfree(pw->fds);
++    sfree(pw);
++}
++
++void pollwrap_clear(pollwrapper *pw)
++{
++    pw->nfd = 0;
++    for (pollwrap_fdtopos *f2p;
++         (f2p = delpos234(pw->fdtopos, 0)) != NULL ;)
++        sfree(f2p);
++}
++
++void pollwrap_add_fd_events(pollwrapper *pw, int fd, int events)
++{
++    pollwrap_fdtopos *f2p, f2p_find;
++
++    assert(fd >= 0);
++
++    f2p_find.fd = fd;
++    f2p = find234(pw->fdtopos, &f2p_find, NULL);
++    if (!f2p) {
++        if (pw->nfd >= pw->fdsize) {
++            pw->fdsize = pw->nfd * 5 / 4 + 32;
++            pw->fds = sresize(pw->fds, pw->fdsize, struct pollfd);
++        }
++        size_t index = pw->nfd++;
++        pw->fds[index].fd = fd;
++        pw->fds[index].events = pw->fds[index].revents = 0;
++
++        f2p = snew(pollwrap_fdtopos);
++        f2p->fd = fd;
++        f2p->pos = index;
++        pollwrap_fdtopos *added = add234(pw->fdtopos, f2p);
++        assert(added == f2p);
++    }
++
++    pw->fds[f2p->pos].events |= events;
++}
++
++#define SELECT_R_IN (POLLIN  | POLLRDNORM | POLLRDBAND)
++#define SELECT_W_IN (POLLOUT | POLLWRNORM | POLLWRBAND)
++#define SELECT_X_IN (POLLPRI)
++
++#define SELECT_R_OUT (SELECT_R_IN | POLLERR | POLLHUP)
++#define SELECT_W_OUT (SELECT_W_IN | POLLERR)
++#define SELECT_X_OUT (SELECT_X_IN)
++
++void pollwrap_add_fd_rwx(pollwrapper *pw, int fd, int rwx)
++{
++    int events = 0;
++    if (rwx & SELECT_R)
++        events |= SELECT_R_IN;
++    if (rwx & SELECT_W)
++        events |= SELECT_W_IN;
++    if (rwx & SELECT_X)
++        events |= SELECT_X_IN;
++    pollwrap_add_fd_events(pw, fd, events);
++}
++
++int pollwrap_poll_instant(pollwrapper *pw)
++{
++    return poll(pw->fds, pw->nfd, 0);
++}
++
++int pollwrap_poll_endless(pollwrapper *pw)
++{
++    return poll(pw->fds, pw->nfd, -1);
++}
++
++int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds)
++{
++    assert(milliseconds >= 0);
++    return poll(pw->fds, pw->nfd, milliseconds);
++}
++
++int pollwrap_get_fd_events(pollwrapper *pw, int fd)
++{
++    pollwrap_fdtopos *f2p, f2p_find;
++
++    assert(fd >= 0);
++
++    f2p_find.fd = fd;
++    f2p = find234(pw->fdtopos, &f2p_find, NULL);
++    if (!f2p)
++        return 0;
++
++    return pw->fds[f2p->pos].revents;
++}
++
++int pollwrap_get_fd_rwx(pollwrapper *pw, int fd)
++{
++    int revents = pollwrap_get_fd_events(pw, fd);
++    int rwx = 0;
++    if (revents & SELECT_R_OUT)
++        rwx |= SELECT_R;
++    if (revents & SELECT_W_OUT)
++        rwx |= SELECT_W;
++    if (revents & SELECT_X_OUT)
++        rwx |= SELECT_X;
++    return rwx;
++}
+diff --git a/unix/uxsel.c b/unix/uxsel.c
+index 52f346a4..a74db11a 100644
+--- a/unix/uxsel.c
++++ b/unix/uxsel.c
+@@ -4,8 +4,8 @@
+  * This module is a sort of all-purpose interchange for file
+  * descriptors. At one end it talks to uxnet.c and pty.c and
+  * anything else which might have one or more fds that need
+- * select()-type things doing to them during an extended program
+- * run; at the other end it talks to pterm.c or uxplink.c or
++ * select() or poll()-type things doing to them during an extended
++ * program run; at the other end it talks to pterm.c or uxplink.c or
+  * anything else which might have its own means of actually doing
+  * those select()-type things.
+  */
+diff --git a/unix/uxsftp.c b/unix/uxsftp.c
+index 65eda985..dd2851e5 100644
+--- a/unix/uxsftp.c
++++ b/unix/uxsftp.c
+@@ -6,6 +6,7 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <stdlib.h>
++#include <limits.h>
+ #include <fcntl.h>
+ #include <dirent.h>
+ #include <unistd.h>
+@@ -13,9 +14,6 @@
+ #include <errno.h>
+ #include <assert.h>
+ #include <glob.h>
+-#ifndef HAVE_NO_SYS_SELECT_H
+-#include <sys/select.h>
+-#endif
+ 
+ #include "putty.h"
+ #include "ssh.h"
+@@ -454,15 +452,16 @@ char *dir_file_cat(const char *dir, const char *file)
+  */
+ static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
+ {
+-    fd_set rset, wset, xset;
+     int i, fdcount, fdsize, *fdlist;
+-    int fd, fdstate, rwx, ret, maxfd;
++    int fd, fdstate, rwx, ret;
+     unsigned long now = GETTICKCOUNT();
+     unsigned long next;
+ 
+     fdlist = NULL;
+     fdcount = fdsize = 0;
+ 
++    pollwrapper *pw = pollwrap_new();
++
+     do {
+ 
+       /* Count the currently active fds. */
+@@ -479,10 +478,7 @@ static int ssh_sftp_do_select(int include_stdin, int 
no_fds_ok)
+           fdlist = sresize(fdlist, fdsize, int);
+       }
+ 
+-      FD_ZERO(&rset);
+-      FD_ZERO(&wset);
+-      FD_ZERO(&xset);
+-      maxfd = 0;
++        pollwrap_clear(pw);
+ 
+       /*
+        * Add all currently open fds to the select sets, and store
+@@ -492,29 +488,20 @@ static int ssh_sftp_do_select(int include_stdin, int 
no_fds_ok)
+       for (fd = first_fd(&fdstate, &rwx); fd >= 0;
+            fd = next_fd(&fdstate, &rwx)) {
+           fdlist[fdcount++] = fd;
+-          if (rwx & 1)
+-              FD_SET_MAX(fd, maxfd, rset);
+-          if (rwx & 2)
+-              FD_SET_MAX(fd, maxfd, wset);
+-          if (rwx & 4)
+-              FD_SET_MAX(fd, maxfd, xset);
++            pollwrap_add_fd_rwx(pw, fd, rwx);
+       }
+ 
+       if (include_stdin)
+-          FD_SET_MAX(0, maxfd, rset);
++          pollwrap_add_fd_rwx(pw, 0, SELECT_R);
+ 
+         if (toplevel_callback_pending()) {
+-            struct timeval tv;
+-            tv.tv_sec = 0;
+-            tv.tv_usec = 0;
+-            ret = select(maxfd, &rset, &wset, &xset, &tv);
++            ret = pollwrap_poll_instant(pw);
+             if (ret == 0)
+                 run_toplevel_callbacks();
+         } else if (run_timers(now, &next)) {
+             do {
+                 unsigned long then;
+                 long ticks;
+-                struct timeval tv;
+ 
+               then = now;
+               now = GETTICKCOUNT();
+@@ -522,38 +509,44 @@ static int ssh_sftp_do_select(int include_stdin, int 
no_fds_ok)
+                   ticks = 0;
+               else
+                   ticks = next - now;
+-              tv.tv_sec = ticks / 1000;
+-              tv.tv_usec = ticks % 1000 * 1000;
+-                ret = select(maxfd, &rset, &wset, &xset, &tv);
+-                if (ret == 0)
++
++                int overflow = FALSE;
++                if (ticks > INT_MAX) {
++                    ticks = INT_MAX;
++                    overflow = TRUE;
++                }
++
++                ret = pollwrap_poll_timeout(pw, ticks);
++                if (ret == 0 && !overflow)
+                     now = next;
+                 else
+                     now = GETTICKCOUNT();
+             } while (ret < 0 && errno == EINTR);
+         } else {
+             do {
+-                ret = select(maxfd, &rset, &wset, &xset, NULL);
++                ret = pollwrap_poll_endless(pw);
+             } while (ret < 0 && errno == EINTR);
+         }
+     } while (ret == 0);
+ 
+     if (ret < 0) {
+-      perror("select");
++      perror("poll");
+       exit(1);
+     }
+ 
+     for (i = 0; i < fdcount; i++) {
+       fd = fdlist[i];
++        int rwx = pollwrap_get_fd_rwx(pw, fd);
+       /*
+        * We must process exceptional notifications before
+        * ordinary readability ones, or we may go straight
+        * past the urgent marker.
+        */
+-      if (FD_ISSET(fd, &xset))
++      if (rwx & SELECT_X)
+           select_result(fd, SELECT_X);
+-      if (FD_ISSET(fd, &rset))
++      if (rwx & SELECT_R)
+           select_result(fd, SELECT_R);
+-      if (FD_ISSET(fd, &wset))
++      if (rwx & SELECT_W)
+           select_result(fd, SELECT_W);
+     }
+ 
+@@ -561,7 +554,9 @@ static int ssh_sftp_do_select(int include_stdin, int 
no_fds_ok)
+ 
+     run_toplevel_callbacks();
+ 
+-    return FD_ISSET(0, &rset) ? 1 : 0;
++    int toret = pollwrap_check_fd_rwx(pw, 0, SELECT_R) ? 1 : 0;
++    pollwrap_free(pw);
++    return toret;
+ }
+ 
+ /*
diff -Nru putty-0.70/debian/patches/uxnet-clean-up-callbacks.patch 
putty-0.70/debian/patches/uxnet-clean-up-callbacks.patch
--- putty-0.70/debian/patches/uxnet-clean-up-callbacks.patch    1970-01-01 
01:00:00.000000000 +0100
+++ putty-0.70/debian/patches/uxnet-clean-up-callbacks.patch    2019-03-17 
09:36:52.000000000 +0000
@@ -0,0 +1,32 @@
+From 5443fe621319e044a45c30fa3ef2fb7df7c482b4 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Tue, 29 Jan 2019 20:15:43 +0000
+Subject: uxnet: clean up callbacks when closing a NetSocket.
+
+uxnet.c's method for passing socket errors on to the Plug involves
+setting up a toplevel callback using the NetSocket itself as the
+context. Therefore, it should call delete_callbacks_for_context when
+it destroys a NetSocket. For example, if _two_ socket errors manage to
+occur, and the first one causes the socket to be closed, you need the
+second callback to not happen, or it'll dereference the freed pointer.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=8329d192be5b2bcd1be61634e0b689ecf2a810a8
+Last-Update: 2019-03-16
+
+Patch-Name: uxnet-clean-up-callbacks.patch
+---
+ unix/uxnet.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/unix/uxnet.c b/unix/uxnet.c
+index ddcd9228..6549eca5 100644
+--- a/unix/uxnet.c
++++ b/unix/uxnet.c
+@@ -1028,6 +1028,7 @@ static void sk_tcp_close(Socket sock)
+     close(s->s);
+     if (s->addr)
+         sk_addr_free(s->addr);
++    delete_callbacks_for_context(s);
+     sfree(s);
+ }
+ 
diff -Nru putty-0.70/debian/patches/uxsel-enum.patch 
putty-0.70/debian/patches/uxsel-enum.patch
--- putty-0.70/debian/patches/uxsel-enum.patch  1970-01-01 01:00:00.000000000 
+0100
+++ putty-0.70/debian/patches/uxsel-enum.patch  2019-03-17 09:36:53.000000000 
+0000
@@ -0,0 +1,241 @@
+From f40787e64d93fd747bb354fbef8eb6eba16ba323 Mon Sep 17 00:00:00 2001
+From: Simon Tatham <ana...@pobox.com>
+Date: Thu, 7 Feb 2019 18:13:56 +0000
+Subject: Introduce an enum of the uxsel / select_result flags.
+
+Those magic numbers 1,2,4 were getting annoying. Time to replace them
+while I can still remember what they do.
+
+Origin: upstream, 
https://git.tartarus.org/?p=simon/putty.git;a=commitdiff;h=47202c4e163373736a5bce01730bebc2d9e878fd
+Last-Update: 2019-03-17
+
+Patch-Name: uxsel-enum.patch
+---
+ unix/gtkcomm.c  | 12 ++++++------
+ unix/unix.h     |  1 +
+ unix/uxagentc.c |  4 ++--
+ unix/uxnet.c    | 14 +++++++-------
+ unix/uxpgnt.c   |  6 +++---
+ unix/uxplink.c  |  6 +++---
+ unix/uxpty.c    | 10 +++++-----
+ unix/uxsftp.c   |  6 +++---
+ 8 files changed, 30 insertions(+), 29 deletions(-)
+
+diff --git a/unix/gtkcomm.c b/unix/gtkcomm.c
+index 28884654..aa7acd3f 100644
+--- a/unix/gtkcomm.c
++++ b/unix/gtkcomm.c
+@@ -82,11 +82,11 @@ gboolean fd_input_func(GIOChannel *source, GIOCondition 
condition,
+      * marker.
+      */
+     if (condition & G_IO_PRI)
+-        select_result(sourcefd, 4);
++        select_result(sourcefd, SELECT_X);
+     if (condition & G_IO_IN)
+-        select_result(sourcefd, 1);
++        select_result(sourcefd, SELECT_R);
+     if (condition & G_IO_OUT)
+-        select_result(sourcefd, 2);
++        select_result(sourcefd, SELECT_W);
+ 
+     return TRUE;
+ }
+@@ -94,11 +94,11 @@ gboolean fd_input_func(GIOChannel *source, GIOCondition 
condition,
+ void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
+ {
+     if (condition & GDK_INPUT_EXCEPTION)
+-        select_result(sourcefd, 4);
++        select_result(sourcefd, SELECT_X);
+     if (condition & GDK_INPUT_READ)
+-        select_result(sourcefd, 1);
++        select_result(sourcefd, SELECT_R);
+     if (condition & GDK_INPUT_WRITE)
+-        select_result(sourcefd, 2);
++        select_result(sourcefd, SELECT_W);
+ }
+ #endif
+ 
+diff --git a/unix/unix.h b/unix/unix.h
+index f21d23ff..0bd012cb 100644
+--- a/unix/unix.h
++++ b/unix/unix.h
+@@ -183,6 +183,7 @@ void uxsel_init(void);
+ typedef void (*uxsel_callback_fn)(int fd, int event);
+ void uxsel_set(int fd, int rwx, uxsel_callback_fn callback);
+ void uxsel_del(int fd);
++enum { SELECT_R = 1, SELECT_W = 2, SELECT_X = 4 };
+ void select_result(int fd, int event);
+ int first_fd(int *state, int *rwx);
+ int next_fd(int *state, int *rwx);
+diff --git a/unix/uxagentc.c b/unix/uxagentc.c
+index 51f9a1eb..8f505aab 100644
+--- a/unix/uxagentc.c
++++ b/unix/uxagentc.c
+@@ -102,7 +102,7 @@ static void agent_select_result(int fd, int event)
+ {
+     agent_pending_query *conn;
+ 
+-    assert(event == 1);                      /* not selecting for anything 
but R */
++    assert(event == SELECT_R);  /* not selecting for anything but R */
+ 
+     conn = find234(agent_pending_queries, &fd, agent_connfind);
+     if (!conn) {
+@@ -197,7 +197,7 @@ agent_pending_query *agent_query(
+       agent_pending_queries = newtree234(agent_conncmp);
+     add234(agent_pending_queries, conn);
+ 
+-    uxsel_set(sock, 1, agent_select_result);
++    uxsel_set(sock, SELECT_R, agent_select_result);
+     return conn;
+ 
+     failure:
+diff --git a/unix/uxnet.c b/unix/uxnet.c
+index 35a90ac7..f2e35b38 100644
+--- a/unix/uxnet.c
++++ b/unix/uxnet.c
+@@ -1284,7 +1284,7 @@ static void net_select_result(int fd, int event)
+     noise_ultralight(event);
+ 
+     switch (event) {
+-      case 4:                        /* exceptional */
++      case SELECT_X:                   /* exceptional */
+       if (!s->oobinline) {
+           /*
+            * On a non-oobinline socket, this indicates that we
+@@ -1321,7 +1321,7 @@ static void net_select_result(int fd, int event)
+        */
+       s->oobpending = TRUE;
+         break;
+-      case 1:                                /* readable; also acceptance */
++      case SELECT_R:                   /* readable; also acceptance */
+       if (s->listener) {
+           /*
+            * On a listening socket, the readability event means a
+@@ -1398,7 +1398,7 @@ static void net_select_result(int fd, int event)
+           plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
+       }
+       break;
+-      case 2:                        /* writable */
++      case SELECT_W:                   /* writable */
+       if (!s->connected) {
+           /*
+            * select() reports a socket as _writable_ when an
+@@ -1536,14 +1536,14 @@ static void uxsel_tell(Actual_Socket s)
+     int rwx = 0;
+     if (!s->pending_error) {
+         if (s->listener) {
+-            rwx |= 1;                  /* read == accept */
++            rwx |= SELECT_R;           /* read == accept */
+         } else {
+             if (!s->connected)
+-                rwx |= 2;              /* write == connect */
++                rwx |= SELECT_W;       /* write == connect */
+             if (s->connected && !s->frozen && !s->incomingeof)
+-                rwx |= 1 | 4;          /* read, except */
++                rwx |= SELECT_R | SELECT_X;
+             if (bufchain_size(&s->output_data))
+-                rwx |= 2;              /* write */
++                rwx |= SELECT_W;
+         }
+     }
+     uxsel_set(s->s, rwx, net_select_result);
+diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c
+index cb85b160..eba08632 100644
+--- a/unix/uxpgnt.c
++++ b/unix/uxpgnt.c
+@@ -945,11 +945,11 @@ void run_agent(void)
+              * past the urgent marker.
+              */
+           if (FD_ISSET(fd, &xset))
+-              select_result(fd, 4);
++              select_result(fd, SELECT_X);
+           if (FD_ISSET(fd, &rset))
+-              select_result(fd, 1);
++              select_result(fd, SELECT_R);
+           if (FD_ISSET(fd, &wset))
+-              select_result(fd, 2);
++              select_result(fd, SELECT_W);
+       }
+ 
+         if (signalpipe[0] >= 0 && FD_ISSET(signalpipe[0], &rset)) {
+diff --git a/unix/uxplink.c b/unix/uxplink.c
+index e891d66a..e7289bf2 100644
+--- a/unix/uxplink.c
++++ b/unix/uxplink.c
+@@ -1142,11 +1142,11 @@ int main(int argc, char **argv)
+              * past the urgent marker.
+              */
+           if (FD_ISSET(fd, &xset))
+-              select_result(fd, 4);
++              select_result(fd, SELECT_X);
+           if (FD_ISSET(fd, &rset))
+-              select_result(fd, 1);
++              select_result(fd, SELECT_R);
+           if (FD_ISSET(fd, &wset))
+-              select_result(fd, 2);
++              select_result(fd, SELECT_W);
+       }
+ 
+       if (FD_ISSET(signalpipe[0], &rset)) {
+diff --git a/unix/uxpty.c b/unix/uxpty.c
+index 618fe9bd..8be507d5 100644
+--- a/unix/uxpty.c
++++ b/unix/uxpty.c
+@@ -600,7 +600,7 @@ void pty_real_select_result(Pty pty, int event, int status)
+           finished = TRUE;
+       }
+     } else {
+-      if (event == 1) {
++      if (event == SELECT_R) {
+ 
+           ret = read(pty->master_fd, buf, sizeof(buf));
+ 
+@@ -627,7 +627,7 @@ void pty_real_select_result(Pty pty, int event, int status)
+           } else if (ret > 0) {
+               from_backend(pty->frontend, 0, buf, ret);
+           }
+-      } else if (event == 2) {
++      } else if (event == SELECT_W) {
+             /*
+              * Attempt to send data down the pty.
+              */
+@@ -707,9 +707,9 @@ static void pty_uxsel_setup(Pty pty)
+ {
+     int rwx;
+ 
+-    rwx = 1;                           /* always want to read from pty */
++    rwx = SELECT_R;                    /* always want to read from pty */
+     if (bufchain_size(&pty->output_data))
+-        rwx |= 2;                      /* might also want to write to it */
++        rwx |= SELECT_W;               /* might also want to write to it */
+     uxsel_set(pty->master_fd, rwx, pty_select_result);
+ 
+     /*
+@@ -717,7 +717,7 @@ static void pty_uxsel_setup(Pty pty)
+      * backend instances, but it's simplest just to call it every
+      * time; uxsel won't mind.
+      */
+-    uxsel_set(pty_signal_pipe[0], 1, pty_select_result);
++    uxsel_set(pty_signal_pipe[0], SELECT_R, pty_select_result);
+ }
+ 
+ /*
+diff --git a/unix/uxsftp.c b/unix/uxsftp.c
+index 56f7a136..65eda985 100644
+--- a/unix/uxsftp.c
++++ b/unix/uxsftp.c
+@@ -550,11 +550,11 @@ static int ssh_sftp_do_select(int include_stdin, int 
no_fds_ok)
+        * past the urgent marker.
+        */
+       if (FD_ISSET(fd, &xset))
+-          select_result(fd, 4);
++          select_result(fd, SELECT_X);
+       if (FD_ISSET(fd, &rset))
+-          select_result(fd, 1);
++          select_result(fd, SELECT_R);
+       if (FD_ISSET(fd, &wset))
+-          select_result(fd, 2);
++          select_result(fd, SELECT_W);
+     }
+ 
+     sfree(fdlist);

--- End Message ---
--- Begin Message ---
On Mon, Mar 18, 2019 at 09:21:38AM +0000, Colin Watson wrote:
> The EU recently funded a bug bounty for PuTTY, and PuTTY 0.71 was
> released over the weekend including a large number of security fixes
> many of which were found by that.  Since this is too late for buster,
> the upstream maintainer kindly sent me a backported patch series which
> he recommended that we apply to 0.70, and I uploaded that to unstable
> yesterday.  I think we should have this in buster, so please unblock.

Unblocked; thanks.


-- 
Jonathan Wiltshire                                      j...@debian.org
Debian Developer                         http://people.debian.org/~jmw

4096R: 0xD3524C51 / 0A55 B7C5 1223 3942 86EC  74C3 5394 479D D352 4C51

--- End Message ---

Reply via email to