Bug 24942

Summary: tftp client timeout failure
Product: Base System Reporter: yxiao <yxiao>
Component: miscAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description yxiao 2001-02-08 01:20:00 UTC
Alarm clock can only fired once. Problem: SIGALRM blocked.

Fix: 

change timer function:
--- tftp.c      2000/08/01 20:38:08     1.2
+++ tftp.c      2001/02/07 16:18:03
@@ -455,9 +455,15 @@
 timer(sig)
        int sig;
 {
+       sigset_t sigset;
+        int error;

+       //signal(SIGALRM, timer);
+       error = sigemptyset(&sigset);
+        error |= sigdelset(&sigset, SIGALRM);
+        error |= sigprocmask(SIG_SETMASK, &sigset, NULL);
        timeout += rexmtval;
-       if (timeout >= maxtimeout) {
+       if (timeout >= maxtimeout || error) {
                printf("Transfer timed out.\n");
                global_errno = ETIMEDOUT;
                longjmp(toplevel, -1);
Comment 1 peter 2001-02-08 02:20:54 UTC
yxiao@cisco.com wrote:
[..]
> >Release:        $FreeBSD: src/usr.bin/tftp/tftp.c,v 1.5 1999/08/28 01:06:24 
    peter Exp
> >Environment:
> Linux dhcp-171-70-93-24.cisco.com 2.2.14-5.0 #1 Tue Mar 7 21:07:39 EST 2000 i
    686 unknown

Are you running our tftpd under Linux?  Or is that just the send-pr
environment?

> >Description:
> Alarm clock can only fired once. Problem: SIGALRM blocked.

The patch below has a couple of problems:

> >Fix:
> change timer function:
> --- tftp.c      2000/08/01 20:38:08     1.2
> +++ tftp.c      2001/02/07 16:18:03
> @@ -455,9 +455,15 @@
>  timer(sig)
>         int sig;
>  {
> +       sigset_t sigset;
> +        int error;
> 
> +       //signal(SIGALRM, timer);
> +       error = sigemptyset(&sigset);
> +        error |= sigdelset(&sigset, SIGALRM);
> +        error |= sigprocmask(SIG_SETMASK, &sigset, NULL);
>         timeout += rexmtval;
> -       if (timeout >= maxtimeout) {
> +       if (timeout >= maxtimeout || error) {
>                 printf("Transfer timed out.\n");
>                 global_errno = ETIMEDOUT;
>                 longjmp(toplevel, -1);

sigdelset() to delete a signal from an already empty set is a NOP.
It might as well be:
	error = sigemptyset(&sigset);
	error |= sigprocmask(SIG_SETMASK, &sigset, NULL);
There is also a problem.. if sigemptyset() returned an error, sigset would
have undefined contents. And yet you call sigprocmask() anyway.

I think a more correct fix would be to use sigsetjmp()/siglongjmp()
to save/restore the mask..  However, according to the man pages, that
shouldn't be necessary:

     The setjmp()/longjmp() pairs save and restore the signal mask while
     _setjmp()/_longjmp() pairs save and restore only the register set and the
     stack.  (See sigprocmask(2).)

     The sigsetjmp()/siglongjmp() function pairs save and restore the signal
     mask if the argument savemask is non-zero, otherwise only the register
     set and the stack are saved.

Cheers,
-Peter
--
Peter Wemm - peter@FreeBSD.org; peter@yahoo-inc.com; peter@netplex.com.au
"All of this is for nothing if we don't go to the stars" - JMS/B5
Comment 2 iedowse freebsd_committer freebsd_triage 2001-11-19 00:43:53 UTC
State Changed
From-To: open->closed


This seems to be a non-problem; the timer function works fine on 
FreeBSD. Below is what happens with an unresponsive host and some 
extra instrumentation in timer() that outputs 'timer': 

tftp> get goo 
timer 
timer 
timer 
timer 
timer 
Transfer timed out. 

tftp>