Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sctp: move the format error check out of __sctp_sf_do_9_1_abort

When T2 timer is to be stopped, the asoc should also be deleted,
otherwise, there will be no chance to call sctp_association_free
and the asoc could last in memory forever.

However, in sctp_sf_shutdown_sent_abort(), after adding the cmd
SCTP_CMD_TIMER_STOP for T2 timer, it may return error due to the
format error from __sctp_sf_do_9_1_abort() and miss adding
SCTP_CMD_ASSOC_FAILED where the asoc will be deleted.

This patch is to fix it by moving the format error check out of
__sctp_sf_do_9_1_abort(), and do it before adding the cmd
SCTP_CMD_TIMER_STOP for T2 timer.

Thanks Hangbin for reporting this issue by the fuzz testing.

v1->v2:
- improve the comment in the code as Marcelo's suggestion.

Fixes: 96ca468b86b0 ("sctp: check invalid value of length parameter in error cause")
Reported-by: Hangbin Liu <liuhangbin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
245709ec 8a9093c7

+20 -9
+20 -9
net/sctp/sm_statefuns.c
··· 170 170 return true; 171 171 } 172 172 173 + /* Check for format error in an ABORT chunk */ 174 + static inline bool sctp_err_chunk_valid(struct sctp_chunk *chunk) 175 + { 176 + struct sctp_errhdr *err; 177 + 178 + sctp_walk_errors(err, chunk->chunk_hdr); 179 + 180 + return (void *)err == (void *)chunk->chunk_end; 181 + } 182 + 173 183 /********************************************************** 174 184 * These are the state functions for handling chunk events. 175 185 **********************************************************/ ··· 2265 2255 sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) 2266 2256 return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); 2267 2257 2258 + if (!sctp_err_chunk_valid(chunk)) 2259 + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); 2260 + 2268 2261 return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); 2269 2262 } 2270 2263 ··· 2310 2297 if (SCTP_ADDR_DEL == 2311 2298 sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) 2312 2299 return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); 2300 + 2301 + if (!sctp_err_chunk_valid(chunk)) 2302 + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); 2313 2303 2314 2304 /* Stop the T2-shutdown timer. */ 2315 2305 sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, ··· 2581 2565 sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) 2582 2566 return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); 2583 2567 2568 + if (!sctp_err_chunk_valid(chunk)) 2569 + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); 2570 + 2584 2571 return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); 2585 2572 } 2586 2573 ··· 2601 2582 2602 2583 /* See if we have an error cause code in the chunk. */ 2603 2584 len = ntohs(chunk->chunk_hdr->length); 2604 - if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) { 2605 - struct sctp_errhdr *err; 2606 - 2607 - sctp_walk_errors(err, chunk->chunk_hdr); 2608 - if ((void *)err != (void *)chunk->chunk_end) 2609 - return sctp_sf_pdiscard(net, ep, asoc, type, arg, 2610 - commands); 2611 - 2585 + if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) 2612 2586 error = ((struct sctp_errhdr *)chunk->skb->data)->cause; 2613 - } 2614 2587 2615 2588 sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); 2616 2589 /* ASSOC_FAILED will DELETE_TCB. */