Command SCTP_CMD_PART_DELIVER issued under memory pressure calls
sctp_ulpq_partial_delivery(), which tries to fetch and partially deliver
the first message it finds without checking if the message is longer than
SCTP_PARTIAL_DELIVERY_POINT. According to the RFC 6458 paragraph 8.1.21.
such a behavior is invalid. Fix it by returning the first message only if
its part currently available is longer than SCTP_PARTIAL_DELIVERY_POINT.

Signed-off-by: Petr Malat <o...@malat.biz>
---
 net/sctp/ulpqueue.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 1c6c640607c5..cada0b7f1548 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -610,6 +610,7 @@ static struct sctp_ulpevent 
*sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
        struct sctp_ulpevent *cevent;
        __u32 ctsn, next_tsn;
        struct sctp_ulpevent *retval;
+       size_t pd_point, pd_len = 0;
 
        /* The chunks are held in the reasm queue sorted by TSN.
         * Walk through the queue sequentially and look for a sequence of
@@ -633,8 +634,9 @@ static struct sctp_ulpevent 
*sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
                                first_frag = pos;
                                next_tsn = ctsn + 1;
                                last_frag = pos;
+                               pd_len = pos->len;
                        } else
-                               goto done;
+                               goto check;
                        break;
 
                case SCTP_DATA_MIDDLE_FRAG:
@@ -643,15 +645,19 @@ static struct sctp_ulpevent 
*sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
                        if (ctsn == next_tsn) {
                                next_tsn++;
                                last_frag = pos;
+                               pd_len += pos->len;
                        } else
-                               goto done;
+                               goto check;
                        break;
 
                case SCTP_DATA_LAST_FRAG:
                        if (!first_frag)
                                return NULL;
-                       else
+                       if (ctsn == next_tsn) {
+                               last_frag = pos;
                                goto done;
+                       } else
+                               goto check;
                        break;
 
                default:
@@ -659,6 +665,11 @@ static struct sctp_ulpevent 
*sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
                }
        }
 
+check:
+       pd_point = sctp_sk(ulpq->asoc->base.sk)->pd_point;
+       if (pd_point && pd_point > pd_len)
+               return NULL;
+
        /* We have the reassembled event. There is no need to look
         * further.
         */
-- 
2.20.1

Reply via email to