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>
master
Richard Cochran 2018-05-16 06:44:56 -07:00
parent cacf272d1c
commit a8b66ce7f2
2 changed files with 12 additions and 4 deletions

10
fd.h
View File

@ -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
View File

@ -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. */