Fix crash in hybrid E2E mode.
Richard Hill reported an occasional NULL pointer deference in
port_delay_request() when in hybrid mode.
	if (p->hybrid_e2e) {
		struct ptp_message *dst = TAILQ_FIRST(&p->best->messages);
		msg->address = dst->address;
		...
	}
The code assumes that the p->best->messages list can't be empty
because:
    The function, port_delay_request(), is called only when
    FD_DELAY_TIMER expires.  That timer is only set by the function,
    port_set_delay_tmo(), which is called:
    1. from process_delay_resp(), but only when state is UNCALIBRATED
       or SLAVE.
    2. from port_e2e_transition(), but only when state is UNCALIBRATED
       or SLAVE.
    Looking at handle_state_decision_event(), a port can only enter
    UNCALIBRATED or SLAVE when it has a valid foreign master record,
    ie p->best->messages is not null.
    A port also only clears p->best->messages when it leaves
    UNCALIBRATED or SLAVE, at which point the FD_DELAY_TIMER is also
    cleared.
*However* the p->best->messages list *can* be empty if the
FD_ANNOUNCE_TIMER and the FD_DELAY_TIMER expire at the same time.  In
this case, the poll() call indicates events on both file descriptors.
The announce timeout is handled like this:
	case FD_ANNOUNCE_TIMER:
        case FD_SYNC_RX_TIMER:
                if (p->best)
                        fc_clear(p->best);
So then the port_delay_request() call de-references the null
TAILQ_FIRST message pointer.
This patch fixes the issue by re-ordering the timer file descriptors
within the polling list.
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Reported-by: Richard Hill <plonta@gmx.de>
			
			
This commit is contained in:
		
							parent
							
								
									cacf272d1c
								
							
						
					
					
						commit
						a8b66ce7f2
					
				
							
								
								
									
										10
									
								
								fd.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								fd.h
									
									
									
									
									
								
							@ -22,12 +22,18 @@
 | 
			
		||||
 | 
			
		||||
#define N_TIMER_FDS 6
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The order matters here.  The DELAY timer must appear before the
 | 
			
		||||
 * ANNOUNCE and SYNC_RX timers in order to correctly handle the case
 | 
			
		||||
 * when the DELAY timer and one of the other two expire during the
 | 
			
		||||
 * same call to poll().
 | 
			
		||||
 */
 | 
			
		||||
enum {
 | 
			
		||||
	FD_EVENT,
 | 
			
		||||
	FD_GENERAL,
 | 
			
		||||
	FD_DELAY_TIMER,
 | 
			
		||||
	FD_ANNOUNCE_TIMER,
 | 
			
		||||
	FD_SYNC_RX_TIMER,
 | 
			
		||||
	FD_DELAY_TIMER,
 | 
			
		||||
	FD_QUALIFICATION_TIMER,
 | 
			
		||||
	FD_MANNO_TIMER,
 | 
			
		||||
	FD_SYNC_TX_TIMER,
 | 
			
		||||
@ -35,6 +41,8 @@ enum {
 | 
			
		||||
	N_POLLFD,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FD_FIRST_TIMER FD_DELAY_TIMER
 | 
			
		||||
 | 
			
		||||
struct fdarray {
 | 
			
		||||
	int fd[N_POLLFD];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								port.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								port.c
									
									
									
									
									
								
							@ -1545,7 +1545,7 @@ void port_disable(struct port *p)
 | 
			
		||||
	transport_close(p->trp, &p->fda);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < N_TIMER_FDS; i++) {
 | 
			
		||||
		close(p->fda.fd[FD_ANNOUNCE_TIMER + i]);
 | 
			
		||||
		close(p->fda.fd[FD_FIRST_TIMER + i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Keep rtnl socket to get link status info. */
 | 
			
		||||
@ -1590,7 +1590,7 @@ int port_initialize(struct port *p)
 | 
			
		||||
		goto no_tropen;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < N_TIMER_FDS; i++) {
 | 
			
		||||
		p->fda.fd[FD_ANNOUNCE_TIMER + i] = fd[i];
 | 
			
		||||
		p->fda.fd[FD_FIRST_TIMER + i] = fd[i];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (port_set_announce_tmo(p))
 | 
			
		||||
@ -1628,7 +1628,7 @@ static int port_renew_transport(struct port *p)
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	transport_close(p->trp, &p->fda);
 | 
			
		||||
	port_clear_fda(p, FD_ANNOUNCE_TIMER);
 | 
			
		||||
	port_clear_fda(p, FD_FIRST_TIMER);
 | 
			
		||||
	res = transport_open(p->trp, p->iface, &p->fda, p->timestamping);
 | 
			
		||||
	/* Need to call clock_fda_changed even if transport_open failed in
 | 
			
		||||
	 * order to update clock to the now closed descriptors. */
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user