]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
stream: improve app-layer data retrieval with GAPs
authorVictor Julien <victor@inliniac.net>
Fri, 13 Dec 2019 21:26:30 +0000 (22:26 +0100)
committerVictor Julien <victor@inliniac.net>
Mon, 20 Jan 2020 14:43:23 +0000 (15:43 +0100)
Don't assume that the next block after the sbb head is after the
requested offset.

If the next block was before the offset, the returned data_len
would underflow and return a nonsense value to the app-layer.

Bug #2993.

src/stream-tcp-reassemble.c

index 7b450db0b732af6a6bbff4b0412c47903bcb4c8e..f8177d54c1ea1920c63912f14c07cb9da72cc279 100644 (file)
@@ -874,6 +874,22 @@ static void GetSessionSize(TcpSession *ssn, Packet *p)
 }
 #endif
 
+static StreamingBufferBlock *GetBlock(StreamingBuffer *sb, const uint64_t offset)
+{
+    StreamingBufferBlock *blk = sb->head;
+    if (blk == NULL)
+        return NULL;
+
+    for ( ; blk != NULL; blk = SBB_RB_NEXT(blk)) {
+        if (blk->offset >= offset)
+            return blk;
+        else if ((blk->offset + blk->len) > offset) {
+            return blk;
+        }
+    }
+    return NULL;
+}
+
 /** \internal
  *
  *  Get buffer, or first part of the buffer if data gaps exist.
@@ -893,33 +909,35 @@ static void GetAppBuffer(TcpStream *stream, const uint8_t **data, uint32_t *data
         *data = mydata;
         *data_len = mydata_len;
     } else {
-        StreamingBufferBlock *blk = stream->sb.head;
+        StreamingBufferBlock *blk = GetBlock(&stream->sb, offset);
+        if (blk == NULL) {
+            *data = NULL;
+            *data_len = 0;
+            return;
+        }
 
-        if (blk->offset > offset) {
+        /* block at expected offset */
+        if (blk->offset == offset) {
+
+            StreamingBufferSBBGetData(&stream->sb, blk, data, data_len);
+
+        /* block past out offset */
+        } else if (blk->offset > offset) {
             SCLogDebug("gap, want data at offset %"PRIu64", "
                     "got data at %"PRIu64". GAP of size %"PRIu64,
                     offset, blk->offset, blk->offset - offset);
             *data = NULL;
             *data_len = blk->offset - offset;
 
-        } else if (offset >= (blk->offset + blk->len)) {
-
-            *data = NULL;
-            StreamingBufferBlock *nblk = SBB_RB_NEXT(blk);
-            *data_len = nblk ? nblk->offset - offset : 0;
-            if (nblk) {
-                SCLogDebug("gap, want data at offset %"PRIu64", "
-                        "got data at %"PRIu64". GAP of size %"PRIu64,
-                        offset, nblk->offset, nblk->offset - offset);
-            }
-
-        } else if (offset > blk->offset && offset < (blk->offset + blk->len)) {
+        /* block starts before offset, but ends after */
+        } else if (offset > blk->offset && offset <= (blk->offset + blk->len)) {
             SCLogDebug("get data from offset %"PRIu64". SBB %"PRIu64"/%u",
                     offset, blk->offset, blk->len);
             StreamingBufferSBBGetDataAtOffset(&stream->sb, blk, data, data_len, offset);
             SCLogDebug("data %p, data_len %u", *data, *data_len);
         } else {
-            StreamingBufferSBBGetData(&stream->sb, blk, data, data_len);
+            *data = NULL;
+            *data_len = 0;
         }
     }
 }