Coverage Report

Created: 2026-06-30 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/app-layer-frames.c
Line
Count
Source
1
/* Copyright (C) 2007-2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 */
24
25
#include "suricata-common.h"
26
#include "util-print.h"
27
28
#include "flow.h"
29
#include "stream-tcp.h"
30
#include "app-layer-frames.h"
31
#include "app-layer-parser.h"
32
33
struct FrameConfig {
34
    SC_ATOMIC_DECLARE(uint64_t, types);
35
};
36
static struct FrameConfig frame_config[ALPROTO_MAX];
37
38
void FrameConfigInit(void)
39
32
{
40
1.15k
    for (AppProto p = 0; p < ALPROTO_MAX; p++) {
41
1.12k
        SC_ATOMIC_INIT(frame_config[p].types);
42
1.12k
    }
43
32
}
44
45
void FrameConfigEnableAll(void)
46
0
{
47
0
    const uint64_t bits = UINT64_MAX;
48
0
    for (AppProto p = 0; p < ALPROTO_MAX; p++) {
49
0
        struct FrameConfig *fc = &frame_config[p];
50
0
        SC_ATOMIC_OR(fc->types, bits);
51
0
    }
52
0
}
53
54
void FrameConfigEnable(const AppProto p, const uint8_t type)
55
43.4k
{
56
43.4k
    const uint64_t bits = BIT_U64(type);
57
43.4k
    struct FrameConfig *fc = &frame_config[p];
58
43.4k
    SC_ATOMIC_OR(fc->types, bits);
59
43.4k
}
60
61
static inline bool FrameConfigTypeIsEnabled(const AppProto p, const uint8_t type)
62
411M
{
63
411M
    struct FrameConfig *fc = &frame_config[p];
64
411M
    const uint64_t bits = BIT_U64(type);
65
411M
    const bool enabled = (SC_ATOMIC_GET(fc->types) & bits) != 0;
66
411M
    return enabled;
67
411M
}
68
69
static void FrameDebug(const char *prefix, const Frames *frames, const Frame *frame)
70
40.6M
{
71
#ifdef DEBUG
72
    const char *type_name = "unknown";
73
    if (frame->type == FRAME_STREAM_TYPE) {
74
        type_name = "stream";
75
    } else if (frames != NULL) {
76
        type_name = AppLayerParserGetFrameNameById(frames->ipproto, frames->alproto, frame->type);
77
    }
78
    SCLogDebug("[%s] %p: frame:%p type:%u/%s id:%" PRIi64 " flags:%02x offset:%" PRIu64
79
               ", len:%" PRIi64 ", inspect_progress:%" PRIu64 ", events:%u %u/%u/%u/%u",
80
            prefix, frames, frame, frame->type, type_name, frame->id, frame->flags, frame->offset,
81
            frame->len, frame->inspect_progress, frame->event_cnt, frame->events[0],
82
            frame->events[1], frame->events[2], frame->events[3]);
83
#endif
84
40.6M
}
85
86
Frame *FrameGetById(Frames *frames, const int64_t id)
87
862k
{
88
862k
    SCLogDebug("frames %p cnt %u, looking for %" PRIi64, frames, frames->cnt, id);
89
12.4M
    for (uint16_t i = 0; i < frames->cnt; i++) {
90
12.3M
        if (i < FRAMES_STATIC_CNT) {
91
1.73M
            Frame *frame = &frames->sframes[i];
92
1.73M
            FrameDebug("get_by_id(static)", frames, frame);
93
1.73M
            if (frame->id == id)
94
548k
                return frame;
95
10.6M
        } else {
96
10.6M
            const uint16_t o = i - FRAMES_STATIC_CNT;
97
10.6M
            Frame *frame = &frames->dframes[o];
98
10.6M
            FrameDebug("get_by_id(dynamic)", frames, frame);
99
10.6M
            if (frame->id == id)
100
258k
                return frame;
101
10.6M
        }
102
12.3M
    }
103
54.8k
    return NULL;
104
862k
}
105
106
Frame *FrameGetByIndex(Frames *frames, const uint32_t idx)
107
9.60M
{
108
9.60M
    if (idx >= frames->cnt)
109
0
        return NULL;
110
111
9.60M
    if (idx < FRAMES_STATIC_CNT) {
112
2.93M
        Frame *frame = &frames->sframes[idx];
113
2.93M
        FrameDebug("get_by_idx(s)", frames, frame);
114
2.93M
        return frame;
115
6.66M
    } else {
116
6.66M
        const uint32_t o = idx - FRAMES_STATIC_CNT;
117
6.66M
        Frame *frame = &frames->dframes[o];
118
6.66M
        FrameDebug("get_by_idx(d)", frames, frame);
119
6.66M
        return frame;
120
6.66M
    }
121
9.60M
}
122
123
static Frame *FrameNew(Frames *frames, uint64_t offset, int64_t len)
124
4.58M
{
125
4.58M
    BUG_ON(frames == NULL);
126
127
4.58M
    if (frames->cnt < FRAMES_STATIC_CNT) {
128
2.15M
        Frame *frame = &frames->sframes[frames->cnt];
129
2.15M
        frames->sframes[frames->cnt].offset = offset;
130
2.15M
        frames->sframes[frames->cnt].len = len;
131
2.15M
        frames->sframes[frames->cnt].id = ++frames->base_id;
132
2.15M
        frames->cnt++;
133
2.15M
        return frame;
134
2.42M
    } else if (frames->dframes == NULL) {
135
26.5k
        BUG_ON(frames->dyn_size != 0);
136
26.5k
        BUG_ON(frames->cnt != FRAMES_STATIC_CNT);
137
138
26.5k
        frames->dframes = SCCalloc(8, sizeof(Frame));
139
26.5k
        if (frames->dframes == NULL) {
140
0
            return NULL;
141
0
        }
142
26.5k
        frames->cnt++;
143
26.5k
        BUG_ON(frames->cnt != FRAMES_STATIC_CNT + 1);
144
145
26.5k
        frames->dyn_size = 8;
146
26.5k
        frames->dframes[0].offset = offset;
147
26.5k
        frames->dframes[0].len = len;
148
26.5k
        frames->dframes[0].id = ++frames->base_id;
149
26.5k
        return &frames->dframes[0];
150
2.39M
    } else {
151
2.39M
        BUG_ON(frames->cnt < FRAMES_STATIC_CNT);
152
153
        /* need to handle dynamic storage of frames now */
154
2.39M
        const uint16_t dyn_cnt = frames->cnt - FRAMES_STATIC_CNT;
155
2.39M
        if (dyn_cnt < frames->dyn_size) {
156
1.60M
            BUG_ON(frames->dframes == NULL);
157
158
            // fall through
159
1.60M
        } else {
160
792k
            if (frames->dyn_size == 256) {
161
772k
                SCLogDebug("limit reached! 256 dynamic frames already");
162
                // limit reached
163
                // TODO figure out if this should lead to an event of sorts
164
772k
                return NULL;
165
772k
            }
166
167
            /* realloc time */
168
19.9k
            uint16_t new_dyn_size = frames->dyn_size * 2;
169
19.9k
            uint32_t new_alloc_size = new_dyn_size * sizeof(Frame);
170
171
19.9k
            void *ptr = SCRealloc(frames->dframes, new_alloc_size);
172
19.9k
            if (ptr == NULL) {
173
0
                return NULL;
174
0
            }
175
176
19.9k
            memset((uint8_t *)ptr + (frames->dyn_size * sizeof(Frame)), 0x00,
177
19.9k
                    (frames->dyn_size * sizeof(Frame)));
178
19.9k
            frames->dframes = ptr;
179
19.9k
            frames->dyn_size = new_dyn_size;
180
19.9k
        }
181
182
1.62M
        frames->cnt++;
183
1.62M
        frames->dframes[dyn_cnt].offset = offset;
184
1.62M
        frames->dframes[dyn_cnt].len = len;
185
1.62M
        frames->dframes[dyn_cnt].id = ++frames->base_id;
186
1.62M
        return &frames->dframes[dyn_cnt];
187
2.39M
    }
188
4.58M
}
189
190
static void FrameClean(Frame *frame)
191
9.29M
{
192
9.29M
    memset(frame, 0, sizeof(*frame));
193
9.29M
}
194
195
static void FrameCopy(Frame *dst, Frame *src)
196
15.0M
{
197
15.0M
    memcpy(dst, src, sizeof(*dst));
198
15.0M
}
199
200
static void AppLayerFrameDumpForFrames(const char *prefix, const Frames *frames)
201
2.40M
{
202
2.40M
    SCLogDebug("prefix: %s", prefix);
203
21.2M
    for (uint16_t i = 0; i < frames->cnt; i++) {
204
18.8M
        if (i < FRAMES_STATIC_CNT) {
205
3.27M
            const Frame *frame = &frames->sframes[i];
206
3.27M
            FrameDebug(prefix, frames, frame);
207
15.5M
        } else {
208
15.5M
            const uint16_t o = i - FRAMES_STATIC_CNT;
209
15.5M
            const Frame *frame = &frames->dframes[o];
210
15.5M
            FrameDebug(prefix, frames, frame);
211
15.5M
        }
212
18.8M
    }
213
2.40M
    SCLogDebug("prefix: %s", prefix);
214
2.40M
}
215
216
static inline uint64_t FrameLeftEdge(const TcpStream *stream, const Frame *frame)
217
99.1M
{
218
99.1M
    const int64_t app_progress = STREAM_APP_PROGRESS(stream);
219
220
99.1M
    const int64_t frame_offset = frame->offset;
221
99.1M
    const int64_t frame_data = app_progress - frame_offset;
222
223
99.1M
    SCLogDebug("frame_offset %" PRIi64 ", frame_data %" PRIi64 ", frame->len %" PRIi64,
224
99.1M
            frame_offset, frame_data, frame->len);
225
99.1M
    BUG_ON(frame_offset > app_progress);
226
227
    /* length unknown, make sure to have at least 2500 */
228
99.1M
    if (frame->len < 0) {
229
6.59M
        if (frame_data <= 2500) {
230
1.30M
            SCLogDebug("got <= 2500 bytes (%" PRIu64 "), returning offset %" PRIu64, frame_data,
231
1.30M
                    frame_offset);
232
1.30M
            return frame_offset;
233
5.28M
        } else {
234
5.28M
            SCLogDebug("got > 2500 bytes (%" PRIu64 "), returning offset %" PRIu64, frame_data,
235
5.28M
                    (frame_offset + (frame_data - 2500)));
236
5.28M
            return frame_offset + (frame_data - 2500);
237
5.28M
        }
238
239
        /* length specified */
240
92.5M
    } else {
241
        /* have all data for the frame, we can skip it */
242
92.5M
        if (frame->len <= frame_data) {
243
33.3M
            uint64_t x = frame_offset + frame_data;
244
33.3M
            SCLogDebug("x %" PRIu64, x);
245
33.3M
            return x;
246
            /*
247
248
                [ stream      <frame_data> ]
249
                             [ frame        .......]
250
251
             */
252
59.2M
        } else if (frame_data < 2500) {
253
56.6M
            uint64_t x = frame_offset;
254
56.6M
            SCLogDebug("x %" PRIu64, x);
255
56.6M
            return x;
256
56.6M
        } else {
257
2.63M
            uint64_t x = frame_offset + (frame_data - 2500);
258
2.63M
            SCLogDebug("x %" PRIu64, x);
259
2.63M
            return x;
260
2.63M
        }
261
92.5M
    }
262
99.1M
}
263
264
/** Stream buffer slides forward, we need to update and age out
265
 *  frame offsets/frames. Aging out means we move existing frames
266
 *  into the slots we'd free up.
267
 *
268
 *  Start:
269
 *
270
 *  [ stream ]
271
 *    [ frame   ...........]
272
 *      offset: 2
273
 *      len: 19
274
 *
275
 *  Slide:
276
 *         [ stream ]
277
 *    [ frame ....          .]
278
 *      offset: 2
279
 *       len: 19
280
 *
281
 *  Slide:
282
 *                [ stream ]
283
 *    [ frame ...........    ]
284
 *      offset: 2
285
 *      len: 19
286
 */
287
static int FrameSlide(const char *ds, Frames *frames, const TcpStream *stream, const uint32_t slide)
288
699k
{
289
699k
    SCLogDebug("start: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64
290
699k
               ", next %" PRIu64,
291
699k
            (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel,
292
699k
            STREAM_BASE_OFFSET(stream), STREAM_BASE_OFFSET(stream) + slide);
293
699k
    BUG_ON(frames == NULL);
294
699k
    SCLogDebug("%s frames %p: sliding %u bytes", ds, frames, slide);
295
699k
    uint64_t le = STREAM_APP_PROGRESS(stream);
296
699k
    const uint64_t next_base = STREAM_BASE_OFFSET(stream) + slide;
297
699k
    const uint16_t start = frames->cnt;
298
699k
    uint16_t removed = 0;
299
699k
    uint16_t x = 0;
300
2.21M
    for (uint16_t i = 0; i < frames->cnt; i++) {
301
1.51M
        if (i < FRAMES_STATIC_CNT) {
302
715k
            Frame *frame = &frames->sframes[i];
303
715k
            FrameDebug("slide(s)", frames, frame);
304
715k
            if (frame->len >= 0 && frame->offset + frame->len <= next_base) {
305
                // remove by not incrementing 'x'
306
15.6k
                SCLogDebug("removing %p id %" PRIi64, frame, frame->id);
307
15.6k
                FrameClean(frame);
308
15.6k
                removed++;
309
699k
            } else {
310
699k
                Frame *nframe = &frames->sframes[x];
311
699k
                FrameCopy(nframe, frame);
312
699k
                if (frame != nframe) {
313
154
                    FrameClean(frame);
314
154
                }
315
699k
                le = MIN(le, FrameLeftEdge(stream, nframe));
316
699k
                x++;
317
699k
            }
318
795k
        } else {
319
795k
            const uint16_t o = i - FRAMES_STATIC_CNT;
320
795k
            Frame *frame = &frames->dframes[o];
321
795k
            FrameDebug("slide(d)", frames, frame);
322
795k
            if (frame->len >= 0 && frame->offset + frame->len <= next_base) {
323
                // remove by not incrementing 'x'
324
5.57k
                SCLogDebug("removing %p id %" PRIi64, frame, frame->id);
325
5.57k
                FrameClean(frame);
326
5.57k
                removed++;
327
790k
            } else {
328
790k
                Frame *nframe;
329
790k
                if (x >= FRAMES_STATIC_CNT) {
330
789k
                    nframe = &frames->dframes[x - FRAMES_STATIC_CNT];
331
789k
                } else {
332
190
                    nframe = &frames->sframes[x];
333
190
                }
334
790k
                FrameCopy(nframe, frame);
335
790k
                if (frame != nframe) {
336
6.73k
                    FrameClean(frame);
337
6.73k
                }
338
790k
                le = MIN(le, FrameLeftEdge(stream, nframe));
339
790k
                x++;
340
790k
            }
341
795k
        }
342
1.51M
    }
343
699k
    frames->cnt = x;
344
699k
    uint64_t o = STREAM_BASE_OFFSET(stream) + slide;
345
699k
    frames->left_edge_rel = le - (STREAM_BASE_OFFSET(stream) + slide);
346
347
#ifdef DEBUG
348
    SCLogDebug("end: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64
349
               " (+slide), cnt %u, removed %u, start %u",
350
            (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream) + slide,
351
            frames->left_edge_rel, STREAM_BASE_OFFSET(stream) + slide, frames->cnt, removed, start);
352
    char pf[32] = "";
353
    snprintf(pf, sizeof(pf), "%s:post_slide", ds);
354
    AppLayerFrameDumpForFrames(pf, frames);
355
#endif
356
699k
    BUG_ON(o > le);
357
699k
    BUG_ON(x != start - removed);
358
699k
    return 0;
359
699k
}
360
361
void AppLayerFramesSlide(Flow *f, const uint32_t slide, const uint8_t direction)
362
1.24M
{
363
1.24M
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
364
1.24M
    if (frames_container == NULL)
365
398k
        return;
366
849k
    Frames *frames;
367
849k
    TcpSession *ssn = f->protoctx;
368
849k
    TcpStream *stream;
369
849k
    if (direction == STREAM_TOSERVER) {
370
305k
        stream = &ssn->client;
371
305k
        frames = &frames_container->toserver;
372
305k
        FrameSlide("toserver", frames, stream, slide);
373
543k
    } else {
374
543k
        stream = &ssn->server;
375
543k
        frames = &frames_container->toclient;
376
543k
        FrameSlide("toclient", frames, stream, slide);
377
543k
    }
378
849k
}
379
380
static void FrameFreeSingleFrame(Frames *frames, Frame *r)
381
662k
{
382
662k
    FrameDebug("free", frames, r);
383
662k
    FrameClean(r);
384
662k
}
385
386
static void FramesClear(Frames *frames)
387
453k
{
388
453k
    BUG_ON(frames == NULL);
389
390
453k
    SCLogDebug("frames %u", frames->cnt);
391
1.11M
    for (uint16_t i = 0; i < frames->cnt; i++) {
392
662k
        if (i < FRAMES_STATIC_CNT) {
393
377k
            Frame *r = &frames->sframes[i];
394
377k
            SCLogDebug("removing frame %p", r);
395
377k
            FrameFreeSingleFrame(frames, r);
396
377k
        } else {
397
285k
            const uint16_t o = i - FRAMES_STATIC_CNT;
398
285k
            Frame *r = &frames->dframes[o];
399
285k
            SCLogDebug("removing frame %p", r);
400
285k
            FrameFreeSingleFrame(frames, r);
401
285k
        }
402
662k
    }
403
453k
    frames->cnt = 0;
404
453k
}
405
406
void FramesFree(Frames *frames)
407
233k
{
408
233k
    BUG_ON(frames == NULL);
409
233k
    FramesClear(frames);
410
233k
    SCFree(frames->dframes);
411
233k
    frames->dframes = NULL;
412
233k
}
413
414
/** \brief create new frame using a pointer to start of the frame
415
 */
416
Frame *AppLayerFrameNewByPointer(Flow *f, const StreamSlice *stream_slice,
417
        const uint8_t *frame_start, const int64_t len, int dir, uint8_t frame_type)
418
771k
{
419
771k
    SCLogDebug("frame_start:%p stream_slice->input:%p stream_slice->offset:%" PRIu64, frame_start,
420
771k
            stream_slice->input, stream_slice->offset);
421
422
771k
    if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type)))
423
654k
        return NULL;
424
425
        /* workarounds for many (unit|fuzz)tests not handling TCP data properly */
426
117k
#if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
427
117k
    if (f->proto == IPPROTO_TCP && f->protoctx == NULL)
428
0
        return NULL;
429
117k
    if (frame_start < stream_slice->input ||
430
117k
            frame_start >= stream_slice->input + stream_slice->input_len)
431
0
        return NULL;
432
117k
#endif
433
117k
    BUG_ON(frame_start < stream_slice->input);
434
117k
    BUG_ON(stream_slice->input == NULL);
435
117k
    BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL);
436
437
117k
    ptrdiff_t ptr_offset = frame_start - stream_slice->input;
438
#ifdef DEBUG
439
    uint64_t offset = ptr_offset + stream_slice->offset;
440
    SCLogDebug("flow %p direction %s frame %p starting at %" PRIu64 " len %" PRIi64
441
               " (offset %" PRIu64 ")",
442
            f, dir == 0 ? "toserver" : "toclient", frame_start, offset, len, stream_slice->offset);
443
#endif
444
117k
    BUG_ON(f->alparser == NULL);
445
446
117k
    FramesContainer *frames_container = AppLayerFramesSetupContainer(f);
447
117k
    if (frames_container == NULL)
448
0
        return NULL;
449
450
117k
    Frames *frames;
451
117k
    if (dir == 0) {
452
22.7k
        frames = &frames_container->toserver;
453
95.0k
    } else {
454
95.0k
        frames = &frames_container->toclient;
455
95.0k
    }
456
457
117k
    uint64_t abs_frame_offset = stream_slice->offset + ptr_offset;
458
459
117k
    Frame *r = FrameNew(frames, abs_frame_offset, len);
460
117k
    if (r != NULL) {
461
117k
        r->type = frame_type;
462
117k
        FrameDebug("new_by_ptr", frames, r);
463
117k
    }
464
117k
    return r;
465
117k
}
466
467
static Frame *AppLayerFrameUdp(Flow *f, const StreamSlice *stream_slice,
468
        const uint32_t frame_start_rel, const int64_t len, int dir, uint8_t frame_type)
469
265k
{
470
265k
    BUG_ON(f->proto != IPPROTO_UDP);
471
472
265k
    if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type)))
473
0
        return NULL;
474
475
265k
    FramesContainer *frames_container = AppLayerFramesSetupContainer(f);
476
265k
    if (frames_container == NULL)
477
0
        return NULL;
478
479
265k
    Frames *frames;
480
265k
    if (dir == 0) {
481
228k
        frames = &frames_container->toserver;
482
228k
    } else {
483
37.5k
        frames = &frames_container->toclient;
484
37.5k
    }
485
486
265k
    Frame *r = FrameNew(frames, frame_start_rel, len);
487
265k
    if (r != NULL) {
488
265k
        r->type = frame_type;
489
265k
    }
490
265k
    return r;
491
265k
}
492
493
/** \brief create new frame using a relative offset from the start of the stream slice
494
 */
495
Frame *AppLayerFrameNewByRelativeOffset(Flow *f, const StreamSlice *stream_slice,
496
        const uint32_t frame_start_rel, const int64_t len, int dir, uint8_t frame_type)
497
151M
{
498
151M
    if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type)))
499
150M
        return NULL;
500
501
        /* workarounds for many (unit|fuzz)tests not handling TCP data properly */
502
951k
#if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
503
951k
    if (f->proto == IPPROTO_TCP && f->protoctx == NULL)
504
0
        return NULL;
505
951k
    if (stream_slice->input == NULL)
506
0
        return NULL;
507
#else
508
    BUG_ON(stream_slice->input == NULL);
509
#endif
510
951k
    BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL);
511
951k
    BUG_ON(f->alparser == NULL);
512
513
951k
    if (f->proto == IPPROTO_UDP) {
514
25.2k
        return AppLayerFrameUdp(f, stream_slice, frame_start_rel, len, dir, frame_type);
515
25.2k
    }
516
517
926k
    FramesContainer *frames_container = AppLayerFramesSetupContainer(f);
518
926k
    if (frames_container == NULL)
519
0
        return NULL;
520
521
926k
    Frames *frames;
522
926k
    if (dir == 0) {
523
518k
        frames = &frames_container->toserver;
524
518k
    } else {
525
408k
        frames = &frames_container->toclient;
526
408k
    }
527
528
926k
    const uint64_t frame_abs_offset = (uint64_t)frame_start_rel + stream_slice->offset;
529
926k
#ifdef DEBUG_VALIDATION
530
926k
    const TcpSession *ssn = f->protoctx;
531
926k
    const TcpStream *stream = dir == 0 ? &ssn->client : &ssn->server;
532
926k
    BUG_ON(stream_slice->offset != STREAM_APP_PROGRESS(stream));
533
926k
    BUG_ON(frame_abs_offset > STREAM_APP_PROGRESS(stream) + stream_slice->input_len);
534
926k
#endif
535
926k
    Frame *r = FrameNew(frames, frame_abs_offset, len);
536
926k
    if (r != NULL) {
537
687k
        r->type = frame_type;
538
687k
    }
539
926k
    return r;
540
926k
}
541
542
void AppLayerFrameDump(Flow *f)
543
1.89M
{
544
1.89M
    if (f->proto == IPPROTO_TCP && f->protoctx && f->alparser) {
545
1.77M
        FramesContainer *frames_container = AppLayerFramesGetContainer(f);
546
1.77M
        if (frames_container != NULL) {
547
1.20M
            AppLayerFrameDumpForFrames("toserver::dump", &frames_container->toserver);
548
1.20M
            AppLayerFrameDumpForFrames("toclient::dump", &frames_container->toclient);
549
1.20M
        }
550
1.77M
    }
551
1.89M
}
552
553
/** \brief create new frame using the absolute offset from the start of the stream
554
 */
555
Frame *AppLayerFrameNewByAbsoluteOffset(Flow *f, const StreamSlice *stream_slice,
556
        const uint64_t frame_start, const int64_t len, int dir, uint8_t frame_type)
557
2.24M
{
558
2.24M
    if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type)))
559
1.85M
        return NULL;
560
561
        /* workarounds for many (unit|fuzz)tests not handling TCP data properly */
562
393k
#if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
563
393k
    if (f->proto == IPPROTO_TCP && f->protoctx == NULL)
564
0
        return NULL;
565
393k
    if (stream_slice->input == NULL)
566
1
        return NULL;
567
#else
568
    BUG_ON(stream_slice->input == NULL);
569
#endif
570
393k
    BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL);
571
393k
    BUG_ON(f->alparser == NULL);
572
393k
    BUG_ON(frame_start < stream_slice->offset);
573
393k
    BUG_ON(frame_start - stream_slice->offset >= (uint64_t)INT_MAX);
574
575
393k
    FramesContainer *frames_container = AppLayerFramesSetupContainer(f);
576
393k
    if (frames_container == NULL)
577
0
        return NULL;
578
579
393k
    Frames *frames;
580
393k
    if (dir == 0) {
581
195k
        frames = &frames_container->toserver;
582
197k
    } else {
583
197k
        frames = &frames_container->toclient;
584
197k
    }
585
586
393k
    SCLogDebug("flow %p direction %s frame type %u offset %" PRIu64 " len %" PRIi64
587
393k
               " (slice offset %" PRIu64 ")",
588
393k
            f, dir == 0 ? "toserver" : "toclient", frame_type, frame_start, len,
589
393k
            stream_slice->offset);
590
393k
    Frame *r = FrameNew(frames, frame_start, len);
591
393k
    if (r != NULL) {
592
391k
        r->type = frame_type;
593
391k
    }
594
393k
    return r;
595
393k
}
596
597
void AppLayerFrameAddEvent(Frame *r, uint8_t e)
598
3.92k
{
599
3.92k
    if (r != NULL) {
600
3.92k
        if (r->event_cnt < 4) { // TODO
601
3.92k
            r->events[r->event_cnt++] = e;
602
3.92k
        }
603
3.92k
        FrameDebug("add_event", NULL, r);
604
3.92k
    }
605
3.92k
}
606
607
void AppLayerFrameAddEventById(Flow *f, const int dir, const FrameId id, uint8_t e)
608
3.36k
{
609
3.36k
    Frame *frame = AppLayerFrameGetById(f, dir, id);
610
3.36k
    AppLayerFrameAddEvent(frame, e);
611
3.36k
}
612
613
FrameId AppLayerFrameGetId(Frame *r)
614
151M
{
615
151M
    if (r != NULL) {
616
712k
        return r->id;
617
150M
    } else {
618
150M
        return -1;
619
150M
    }
620
151M
}
621
622
void AppLayerFrameSetLength(Frame *frame, int64_t len)
623
15.8k
{
624
15.8k
    if (frame != NULL) {
625
15.8k
        frame->len = len;
626
15.8k
        FrameDebug("set_length", NULL, frame);
627
15.8k
    }
628
15.8k
}
629
630
void AppLayerFrameSetLengthById(Flow *f, const int dir, const FrameId id, int64_t len)
631
0
{
632
0
    Frame *frame = AppLayerFrameGetById(f, dir, id);
633
0
    AppLayerFrameSetLength(frame, len);
634
0
}
635
636
void AppLayerFrameSetTxId(Frame *r, uint64_t tx_id)
637
1.18M
{
638
1.18M
    if (r != NULL) {
639
1.18M
        r->flags |= FRAME_FLAG_TX_ID_SET;
640
1.18M
        r->tx_id = tx_id;
641
1.18M
        FrameDebug("set_txid", NULL, r);
642
1.18M
    }
643
1.18M
}
644
645
void AppLayerFrameSetTxIdById(Flow *f, const int dir, const FrameId id, uint64_t tx_id)
646
0
{
647
0
    Frame *frame = AppLayerFrameGetById(f, dir, id);
648
0
    AppLayerFrameSetTxId(frame, tx_id);
649
0
}
650
651
Frame *AppLayerFrameGetById(Flow *f, const int dir, const FrameId frame_id)
652
1.27M
{
653
1.27M
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
654
1.27M
    SCLogDebug("get frame_id %" PRIi64 " direction %u/%s frames_container %p", frame_id, dir,
655
1.27M
            dir == 0 ? "toserver" : "toclient", frames_container);
656
1.27M
    if (frames_container == NULL)
657
457k
        return NULL;
658
659
813k
    Frames *frames;
660
813k
    if (dir == 0) {
661
477k
        frames = &frames_container->toserver;
662
477k
    } else {
663
335k
        frames = &frames_container->toclient;
664
335k
    }
665
813k
    SCLogDebug("frames %p", frames);
666
813k
    return FrameGetById(frames, frame_id);
667
1.27M
}
668
669
static inline bool FrameIsDone(
670
        const Frame *frame, const uint64_t abs_offset, const uint64_t abs_right_edge)
671
97.0M
{
672
    /* frame with negative length means we don't know the size yet. */
673
97.0M
    if (frame->len < 0)
674
4.41M
        return false;
675
676
92.6M
    const int64_t frame_abs_offset = frame->offset;
677
92.6M
    const int64_t frame_right_edge = frame_abs_offset + frame->len;
678
92.6M
    if ((uint64_t)frame_right_edge <= abs_right_edge) {
679
2.88M
        SCLogDebug("frame %p id %" PRIi64 " is done", frame, frame->id);
680
2.88M
        return true;
681
2.88M
    }
682
89.7M
    return false;
683
92.6M
}
684
685
static void FramePrune(Frames *frames, const TcpStream *stream, const bool eof)
686
3.24M
{
687
3.24M
    const uint64_t frames_le_start = (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream);
688
3.24M
    SCLogDebug("start: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64,
689
3.24M
            (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel,
690
3.24M
            STREAM_BASE_OFFSET(stream));
691
3.24M
    const uint64_t abs_offset = STREAM_BASE_OFFSET(stream);
692
3.24M
    const uint64_t acked = StreamTcpGetUsable(stream, eof);
693
3.24M
    uint64_t le = STREAM_APP_PROGRESS(stream);
694
695
3.24M
    const uint16_t start = frames->cnt;
696
3.24M
    uint16_t removed = 0;
697
3.24M
    uint16_t x = 0;
698
17.6M
    for (uint16_t i = 0; i < frames->cnt; i++) {
699
14.4M
        if (i < FRAMES_STATIC_CNT) {
700
3.37M
            Frame *frame = &frames->sframes[i];
701
3.37M
            FrameDebug("prune(s)", frames, frame);
702
3.37M
            if (eof || FrameIsDone(frame, abs_offset, acked)) {
703
                // remove by not incrementing 'x'
704
684k
                SCLogDebug("removing %p id %" PRIi64, frame, frame->id);
705
684k
                FrameDebug("remove(s)", frames, frame);
706
684k
                FrameClean(frame);
707
684k
                removed++;
708
2.69M
            } else {
709
2.69M
                const uint64_t fle = FrameLeftEdge(stream, frame);
710
2.69M
                le = MIN(le, fle);
711
2.69M
                SCLogDebug("le %" PRIu64 ", frame fle %" PRIu64, le, fle);
712
2.69M
                Frame *nframe = &frames->sframes[x];
713
2.69M
                FrameCopy(nframe, frame);
714
2.69M
                if (frame != nframe) {
715
11.6k
                    FrameClean(frame);
716
11.6k
                }
717
2.69M
                x++;
718
2.69M
            }
719
11.0M
        } else {
720
11.0M
            const uint16_t o = i - FRAMES_STATIC_CNT;
721
11.0M
            Frame *frame = &frames->dframes[o];
722
11.0M
            FrameDebug("prune(d)", frames, frame);
723
11.0M
            if (eof || FrameIsDone(frame, abs_offset, acked)) {
724
                // remove by not incrementing 'x'
725
148k
                SCLogDebug("removing %p id %" PRIi64, frame, frame->id);
726
148k
                FrameDebug("remove(d)", frames, frame);
727
148k
                FrameClean(frame);
728
148k
                removed++;
729
10.9M
            } else {
730
10.9M
                const uint64_t fle = FrameLeftEdge(stream, frame);
731
10.9M
                le = MIN(le, fle);
732
10.9M
                SCLogDebug("le %" PRIu64 ", frame fle %" PRIu64, le, fle);
733
10.9M
                Frame *nframe;
734
10.9M
                if (x >= FRAMES_STATIC_CNT) {
735
10.9M
                    nframe = &frames->dframes[x - FRAMES_STATIC_CNT];
736
10.9M
                } else {
737
5.48k
                    nframe = &frames->sframes[x];
738
5.48k
                }
739
10.9M
                FrameCopy(nframe, frame);
740
10.9M
                if (frame != nframe) {
741
9.38k
                    FrameClean(frame);
742
9.38k
                }
743
10.9M
                x++;
744
10.9M
            }
745
11.0M
        }
746
14.4M
    }
747
3.24M
    frames->cnt = x;
748
3.24M
    frames->left_edge_rel = le - STREAM_BASE_OFFSET(stream);
749
#ifdef DEBUG
750
    SCLogDebug("end: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64
751
               ", cnt %u, removed %u, start %u",
752
            (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel,
753
            STREAM_BASE_OFFSET(stream), frames->cnt, removed, start);
754
    AppLayerFrameDumpForFrames("post_slide", frames);
755
#endif
756
3.24M
    BUG_ON(le < STREAM_BASE_OFFSET(stream));
757
3.24M
    if (frames->cnt > 0) { // if we removed all this can fail
758
1.60M
        BUG_ON(frames_le_start > le);
759
1.60M
    }
760
3.24M
    BUG_ON(x != start - removed);
761
3.24M
}
762
763
void FramesPrune(Flow *f, Packet *p)
764
7.43M
{
765
7.43M
    if (f->proto == IPPROTO_TCP && f->protoctx == NULL)
766
0
        return;
767
7.43M
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
768
7.43M
    if (frames_container == NULL)
769
4.17M
        return;
770
771
3.26M
    Frames *frames;
772
773
3.26M
    if (p->proto == IPPROTO_UDP) {
774
19.2k
        SCLogDebug("clearing all UDP frames");
775
19.2k
        if (PKT_IS_TOSERVER(p)) {
776
14.0k
            frames = &frames_container->toserver;
777
14.0k
        } else {
778
5.26k
            frames = &frames_container->toclient;
779
5.26k
        }
780
19.2k
        FramesClear(frames);
781
19.2k
        return;
782
19.2k
    }
783
784
3.24M
    TcpSession *ssn = f->protoctx;
785
786
3.24M
    if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) {
787
3.89k
        AppLayerFramesFreeContainer(f);
788
3.89k
        return;
789
3.89k
    }
790
791
3.24M
    TcpStream *stream;
792
3.24M
    if (PKT_IS_TOSERVER(p)) {
793
1.68M
        stream = &ssn->client;
794
1.68M
        frames = &frames_container->toserver;
795
1.68M
    } else {
796
1.55M
        stream = &ssn->server;
797
1.55M
        frames = &frames_container->toclient;
798
1.55M
    }
799
800
3.24M
    const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p);
801
3.24M
    SCLogDebug("eof %s", eof ? "TRUE" : "false");
802
3.24M
    FramePrune(frames, stream, eof);
803
3.24M
}