NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
bin/51711: ftp(1) SIGSEGV by a race
>Number: 51711
>Category: bin
>Synopsis: ftp(1) SIGSEGV by a race
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Dec 12 14:40:00 +0000 2016
>Originator: KAMADA Ken'ichi
>Release: NetBSD 7.0.1_PATCH
>Organization:
>Environment:
System: NetBSD hisa.nanohz.org 7.0.1_PATCH NetBSD 7.0.1_PATCH (HISA) #27: Sun May 29 19:05:29 JST 2016 ken%hisa.nanohz.org@localhost:/usr/src/sys/arch/amd64/compile/HISA amd64
Architecture: x86_64
Machine: amd64
>Description:
I found ftp.core in /usr/pkgsrc/distfiles.
In the backtrace, address 0 was called from address 0x40e5f4 in
getreply(). There is a call to (*oldsigalrm) at that address, and
it seems that oldsigalrm was SIG_DFL.
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x000000000040e5f4 in getreply ()
#2 0x00000000004120d7 in hookup ()
#3 0x0000000000416c37 in setpeer ()
#4 0x000000000040a293 in fetch_ftp ()
#5 0x000000000040d678 in auto_fetch ()
#6 0x0000000000418af2 in main ()
>How-To-Repeat:
The timing is very severe, so it is not easy to reproduce.
If you were extremely (un)lucky, the following commands would trigger
this crash.
% ruby -e 'require "socket"; sv=TCPServer.open("localhost", 9999); s=sv.accept; s.write("500"); sleep(59.9); s.write("\n"); sleep(5)' &
% ftp ftp://localhost:9999/test.txt
By looking at the code in ftp.c, this will occur in the following scenario.
- the timer is set at [*1],
- the peer is slow and returns '\n' at the last microsecond
before the timer expires,
- the timer fires between [*2] and [*3] and timeoutflag is incremented
in the signal handler, and
- (*oldsigalrm) is called at [*4] even if it is SIG_DFL.
int
getreply(int expecteof)
{
...
for (lineno = 0 ;; lineno++) {
dig = n = code = 0;
cp = current_line;
while (alarmtimer(quit_time ? quit_time : 60), // [*1]
((c = getc(cin)) != '\n')) {
...
}
// [*2]
if (verbose > 0 || ((verbose > -1 && n == '5') &&
(n < '5' || !retry_connect))) {
(void)putc(c, ttyout);
(void)fflush(ttyout);
}
if (cp[-1] == '\r')
cp[-1] = '\0';
*cp = '\0';
if (lineno == 0)
(void)strlcpy(reply_string, current_line,
sizeof(reply_string));
if (lineno > 0 && code == 0 && reply_callback != NULL)
(*reply_callback)(current_line);
if (continuation && code != originalcode) {
if (originalcode == 0)
originalcode = code;
continue;
}
if (n != '1')
cpend = 0;
// [*3]
alarmtimer(0);
(void)xsignal(SIGINT, oldsigint);
(void)xsignal(SIGALRM, oldsigalrm);
if (code == 421 || originalcode == 421)
lostpeer(0);
if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN)
(*oldsigint)(SIGINT);
if (timeoutflag && oldsigalrm != cmdtimeout &&
oldsigalrm != SIG_IGN)
(*oldsigalrm)(SIGALRM); // [*4]
return (n - '0');
}
}
>Fix:
Index: ftp.c
===================================================================
RCS file: /cvsroot/src/usr.bin/ftp/ftp.c,v
retrieving revision 1.164
diff -u -r1.164 ftp.c
--- ftp.c 4 Jul 2012 06:09:37 -0000 1.164
+++ ftp.c 12 Dec 2016 14:32:03 -0000
@@ -114,6 +114,7 @@
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -500,11 +501,23 @@
(void)xsignal(SIGALRM, oldsigalrm);
if (code == 421 || originalcode == 421)
lostpeer(0);
- if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN)
- (*oldsigint)(SIGINT);
- if (timeoutflag && oldsigalrm != cmdtimeout &&
- oldsigalrm != SIG_IGN)
- (*oldsigalrm)(SIGINT);
+ /*
+ * We may receive SIGINT in the very short time range after
+ * the getc()-loop ends and before oldsigint is restored.
+ * In that case, re-raise SIGINT because it is sent from an
+ * external entity (e.g., a user) and the action is expected.
+ */
+ if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN) {
+ if (oldsigint == SIG_DFL)
+ raise(SIGINT);
+ else
+ (*oldsigint)(SIGINT);
+ }
+ /*
+ * Do not re-raise SIGALRM even if timeoutflag is set.
+ * It was from the timer to wait a '\n' from the peer,
+ * and we won the race.
+ */
return (n - '0');
}
}
Home |
Main Index |
Thread Index |
Old Index