Given the strong clues suggesting that it is due to a version-specific regression, here are three ways to get code that doesn't depend on OpenSSH version.
Verification made that only fd 1 is affected and fd 0 and 2 are left clean, permutation between fd 1 and 2 is a possible workaround. In following code, fd 2 become dirty but the defect would not appear unless a sufficient amount of data is written on it, while fd 1 is clean:
(
ssh localhost '(seq 100000) {safe}>&2 2>&1 >&$safe' \
{safe}>&2 2>&1 >&$safe
seq 100000
) | wc
Or using an additional pipe, wrap ssh in a subprocess to isolate the affected file descriptor:
(
ssh localhost seq 100000 | cat
seq 100000
) | wc
May be an acceptable drawback if you have to write an SSH invocation that may share stdout file descriptor with no dependency on OpenSSH affected version.
A 3rd way is to assign the affected fd to a file instead of directly to a pipe. Here is a function to do that :
repipe(){
tmp=$(mktemp)
trap "rm -f $tmp" RETURN
"$@" > $tmp &
tail --pid $! -n+0 -f $tmp
}
(
repipe ssh localhost seq 100000
seq 100000
) | wc
EDIT: https://bugzilla.mindrot.org/show_bug.cgi?id=3280
has been reported on version 8.5 (but existed in 7.9 at least) and closed on version 8.9. If you cannot upgrade your binaries, you may want to put this script with basename ssh
in front of /usr/bin
in your user PATH :
#!/bin/bash
native=/usr/bin/ssh
if [[ ${vnum:=$($native -V 2>&1 | (IFS="[_. ]" read osef maj min osef && printf "%02d.%s" $maj $min))} > 07.9 && $vnum < 08.9 ]]
then
exec $native "$@" | cat
else
[[ $vnum > 08.9 ]] && echo "INFO: $0 may be removed on this host">&2
exec $native "$@"
fi