• Daniel Borkmann's avatar
    net: sctp: fix remote memory pressure from excessive queueing · 3a8c709b
    Daniel Borkmann authored
    commit 26b87c78 upstream.
    
    This scenario is not limited to ASCONF, just taken as one
    example triggering the issue. When receiving ASCONF probes
    in the form of ...
    
      -------------- INIT[ASCONF; ASCONF_ACK] ------------->
      <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------
      -------------------- COOKIE-ECHO -------------------->
      <-------------------- COOKIE-ACK ---------------------
      ---- ASCONF_a; [ASCONF_b; ...; ASCONF_n;] JUNK ------>
      [...]
      ---- ASCONF_m; [ASCONF_o; ...; ASCONF_z;] JUNK ------>
    
    ... where ASCONF_a, ASCONF_b, ..., ASCONF_z are good-formed
    ASCONFs and have increasing serial numbers, we process such
    ASCONF chunk(s) marked with !end_of_packet and !singleton,
    since we have not yet reached the SCTP packet end. SCTP does
    only do verification on a chunk by chunk basis, as an SCTP
    packet is nothing more than just a container of a stream of
    chunks which it eats up one by one.
    
    We could run into the case that we receive a packet with a
    malformed tail, above marked as trailing JUNK. All previous
    chunks are here goodformed, so the stack will eat up all
    previous chunks up to this point. In case JUNK does not fit
    into a chunk header and there are no more other chunks in
    the input queue, or in case JUNK contains a garbage chunk
    header, but the encoded chunk length would exceed the skb
    tail, or we came here from an entirely different scenario
    and the chunk has pdiscard=1 mark (without having had a flush
    point), it will happen, that we will excessively queue up
    the association's output queue (a correct final chunk may
    then turn it into a response flood when flushing the
    queue ;)): I ran a simple script with incremental ASCONF
    serial numbers and could see the server side consuming
    excessive amount of RAM [before/after: up to 2GB and more].
    
    The issue at heart is that the chunk train basically ends
    with !end_of_packet and !singleton markers and since commit
    2e3216cd ("sctp: Follow security requirement of responding
    with 1 packet") therefore preventing an output queue flush
    point in sctp_do_sm() -> sctp_cmd_interpreter() on the input
    chunk (chunk = event_arg) even though local_cork is set,
    but its precedence has changed since then. In the normal
    case, the last chunk with end_of_packet=1 would trigger the
    queue flush to accommodate possible outgoing bundling.
    
    In the input queue, sctp_inq_pop() seems to do the right thing
    in terms of discarding invalid chunks. So, above JUNK will
    not enter the state machine and instead be released and exit
    the sctp_assoc_bh_rcv() chunk processing loop. It's simply
    the flush point being missing at loop exit. Adding a try-flush
    approach on the output queue might not work as the underlying
    infrastructure might be long gone at this point due to the
    side-effect interpreter run.
    
    One possibility, albeit a bit of a kludge, would be to defer
    invalid chunk freeing into the state machine in order to
    possibly trigger packet discards and thus indirectly a queue
    flush on error. It would surely be better to discard chunks
    as in the current, perhaps better controlled environment, but
    going back and forth, it's simply architecturally not possible.
    I tried various trailing JUNK attack cases and it seems to
    look good now.
    
    Joint work with Vlad Yasevich.
    
    Fixes: 2e3216cd ("sctp: Follow security requirement of responding with 1 packet")
    Signed-off-by: default avatarDaniel Borkmann <dborkman@redhat.com>
    Signed-off-by: default avatarVlad Yasevich <vyasevich@gmail.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
    3a8c709b
inqueue.c 6.57 KB