Skip to content

Commit

Permalink
dccp: ccid: move timers to struct dccp_sock
Browse files Browse the repository at this point in the history
When dccps_hc_tx_ccid is freed, ccid timers may still trigger. The reason
del_timer_sync can't be used is because this relies on keeping a reference
to struct sock. But as we keep a pointer to dccps_hc_tx_ccid and free that
during disconnect, the timer should really belong to struct dccp_sock.

This addresses CVE-2020-16119.

Fixes: 839a609 (net: dccp: Convert timers to use timer_setup())
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
  • Loading branch information
Thadeu Lima de Souza Cascardo authored and anthraxx committed Dec 22, 2020
1 parent 75644b8 commit a7d8899
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 23 deletions.
2 changes: 2 additions & 0 deletions include/linux/dccp.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ struct dccp_ackvec;
* @dccps_sync_scheduled - flag which signals "send out-of-band message soon"
* @dccps_xmitlet - tasklet scheduled by the TX CCID to dequeue data packets
* @dccps_xmit_timer - used by the TX CCID to delay sending (rate-based pacing)
* @dccps_ccid_timer - used by the CCIDs
* @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs)
*/
struct dccp_sock {
Expand Down Expand Up @@ -303,6 +304,7 @@ struct dccp_sock {
__u8 dccps_sync_scheduled:1;
struct tasklet_struct dccps_xmitlet;
struct timer_list dccps_xmit_timer;
struct timer_list dccps_ccid_timer;
};

static inline struct dccp_sock *dccp_sk(const struct sock *sk)
Expand Down
32 changes: 19 additions & 13 deletions net/dccp/ccids/ccid2.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,26 @@ static void dccp_tasklet_schedule(struct sock *sk)

static void ccid2_hc_tx_rto_expire(struct timer_list *t)
{
struct ccid2_hc_tx_sock *hc = from_timer(hc, t, tx_rtotimer);
struct sock *sk = hc->sk;
const bool sender_was_blocked = ccid2_cwnd_network_limited(hc);
struct dccp_sock *dp = from_timer(dp, t, dccps_ccid_timer);
struct sock *sk = (struct sock *)dp;
struct ccid2_hc_tx_sock *hc;
bool sender_was_blocked;

bh_lock_sock(sk);

if (inet_sk_state_load(sk) == DCCP_CLOSED)
goto out;

hc = ccid_priv(dp->dccps_hc_tx_ccid);
sender_was_blocked = ccid2_cwnd_network_limited(hc);

if (sock_owned_by_user(sk)) {
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + HZ / 5);
sk_reset_timer(sk, &dp->dccps_ccid_timer, jiffies + HZ / 5);
goto out;
}

ccid2_pr_debug("RTO_EXPIRE\n");

if (sk->sk_state == DCCP_CLOSED)
goto out;

/* back-off timer */
hc->tx_rto <<= 1;
if (hc->tx_rto > DCCP_RTO_MAX)
Expand All @@ -166,7 +171,7 @@ static void ccid2_hc_tx_rto_expire(struct timer_list *t)
if (sender_was_blocked)
dccp_tasklet_schedule(sk);
/* restart backed-off timer */
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
sk_reset_timer(sk, &dp->dccps_ccid_timer, jiffies + hc->tx_rto);
out:
bh_unlock_sock(sk);
sock_put(sk);
Expand Down Expand Up @@ -330,7 +335,7 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len)
}
#endif

sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
sk_reset_timer(sk, &dp->dccps_ccid_timer, jiffies + hc->tx_rto);

#ifdef CONFIG_IP_DCCP_CCID2_DEBUG
do {
Expand Down Expand Up @@ -700,9 +705,9 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)

/* restart RTO timer if not all outstanding data has been acked */
if (hc->tx_pipe == 0)
sk_stop_timer(sk, &hc->tx_rtotimer);
sk_stop_timer(sk, &dp->dccps_ccid_timer);
else
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
sk_reset_timer(sk, &dp->dccps_ccid_timer, jiffies + hc->tx_rto);
done:
/* check if incoming Acks allow pending packets to be sent */
if (sender_was_blocked && !ccid2_cwnd_network_limited(hc))
Expand Down Expand Up @@ -737,17 +742,18 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
hc->tx_last_cong = hc->tx_lsndtime = hc->tx_cwnd_stamp = ccid2_jiffies32;
hc->tx_cwnd_used = 0;
hc->sk = sk;
timer_setup(&hc->tx_rtotimer, ccid2_hc_tx_rto_expire, 0);
timer_setup(&dp->dccps_ccid_timer, ccid2_hc_tx_rto_expire, 0);
INIT_LIST_HEAD(&hc->tx_av_chunks);
return 0;
}

static void ccid2_hc_tx_exit(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
int i;

sk_stop_timer(sk, &hc->tx_rtotimer);
sk_stop_timer(sk, &dp->dccps_ccid_timer);

for (i = 0; i < hc->tx_seqbufc; i++)
kfree(hc->tx_seqbuf[i]);
Expand Down
30 changes: 20 additions & 10 deletions net/dccp/ccids/ccid3.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,24 @@ static inline void ccid3_hc_tx_update_win_count(struct ccid3_hc_tx_sock *hc,

static void ccid3_hc_tx_no_feedback_timer(struct timer_list *t)
{
struct ccid3_hc_tx_sock *hc = from_timer(hc, t, tx_no_feedback_timer);
struct sock *sk = hc->sk;
struct dccp_sock *dp = from_timer(dp, t, dccps_ccid_timer);
struct ccid3_hc_tx_sock *hc;
struct sock *sk = (struct sock *)dp;
unsigned long t_nfb = USEC_PER_SEC / 5;

bh_lock_sock(sk);

if (inet_sk_state_load(sk) == DCCP_CLOSED)
goto out;

if (sock_owned_by_user(sk)) {
/* Try again later. */
/* XXX: set some sensible MIB */
goto restart_timer;
}

hc = ccid_priv(dp->dccps_hc_tx_ccid);

ccid3_pr_debug("%s(%p, state=%s) - entry\n", dccp_role(sk), sk,
ccid3_tx_state_name(hc->tx_state));

Expand Down Expand Up @@ -250,8 +257,8 @@ static void ccid3_hc_tx_no_feedback_timer(struct timer_list *t)
t_nfb = max(hc->tx_t_rto, 2 * hc->tx_t_ipi);

restart_timer:
sk_reset_timer(sk, &hc->tx_no_feedback_timer,
jiffies + usecs_to_jiffies(t_nfb));
sk_reset_timer(sk, &dp->dccps_ccid_timer,
jiffies + usecs_to_jiffies(t_nfb));
out:
bh_unlock_sock(sk);
sock_put(sk);
Expand Down Expand Up @@ -280,7 +287,7 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
return -EBADMSG;

if (hc->tx_state == TFRC_SSTATE_NO_SENT) {
sk_reset_timer(sk, &hc->tx_no_feedback_timer, (jiffies +
sk_reset_timer(sk, &dp->dccps_ccid_timer, (jiffies +
usecs_to_jiffies(TFRC_INITIAL_TIMEOUT)));
hc->tx_last_win_count = 0;
hc->tx_t_last_win_count = now;
Expand Down Expand Up @@ -354,6 +361,7 @@ static void ccid3_hc_tx_packet_sent(struct sock *sk, unsigned int len)
static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
{
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
struct dccp_sock *dp = dccp_sk(sk);
struct tfrc_tx_hist_entry *acked;
ktime_t now;
unsigned long t_nfb;
Expand Down Expand Up @@ -420,7 +428,7 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
(unsigned int)(hc->tx_x >> 6));

/* unschedule no feedback timer */
sk_stop_timer(sk, &hc->tx_no_feedback_timer);
sk_stop_timer(sk, &dp->dccps_ccid_timer);

/*
* As we have calculated new ipi, delta, t_nom it is possible
Expand All @@ -445,8 +453,8 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
"expire in %lu jiffies (%luus)\n",
dccp_role(sk), sk, usecs_to_jiffies(t_nfb), t_nfb);

sk_reset_timer(sk, &hc->tx_no_feedback_timer,
jiffies + usecs_to_jiffies(t_nfb));
sk_reset_timer(sk, &dp->dccps_ccid_timer,
jiffies + usecs_to_jiffies(t_nfb));
}

static int ccid3_hc_tx_parse_options(struct sock *sk, u8 packet_type,
Expand Down Expand Up @@ -488,21 +496,23 @@ static int ccid3_hc_tx_parse_options(struct sock *sk, u8 packet_type,

static int ccid3_hc_tx_init(struct ccid *ccid, struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
struct ccid3_hc_tx_sock *hc = ccid_priv(ccid);

hc->tx_state = TFRC_SSTATE_NO_SENT;
hc->tx_hist = NULL;
hc->sk = sk;
timer_setup(&hc->tx_no_feedback_timer,
timer_setup(&dp->dccps_ccid_timer,
ccid3_hc_tx_no_feedback_timer, 0);
return 0;
}

static void ccid3_hc_tx_exit(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);

sk_stop_timer(sk, &hc->tx_no_feedback_timer);
sk_stop_timer(sk, &dp->dccps_ccid_timer);
tfrc_tx_hist_purge(&hc->tx_hist);
}

Expand Down

0 comments on commit a7d8899

Please sign in to comment.