Coverage Report

Created: 2025-08-03 06:27

/src/dovecot/src/lib-test/test-istream.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "memarea.h"
5
#include "istream-private.h"
6
#include "test-common.h"
7
8
struct test_istream {
9
  struct istream_private istream;
10
  const void *orig_buffer;
11
  unsigned int skip_diff;
12
  size_t max_pos;
13
  bool allow_eof;
14
};
15
16
static void test_buffer_free(unsigned char *buf)
17
37.6k
{
18
37.6k
  i_free(buf);
19
37.6k
}
20
21
static ssize_t test_read(struct istream_private *stream)
22
37.7k
{
23
37.7k
  struct test_istream *tstream = (struct test_istream *)stream;
24
37.7k
  unsigned int new_skip_diff;
25
37.7k
  size_t cur_max;
26
37.7k
  ssize_t ret;
27
28
37.7k
  i_assert(stream->skip <= stream->pos);
29
30
37.7k
  if (stream->pos - stream->skip >= tstream->istream.max_buffer_size) {
31
0
    i_assert(stream->skip != stream->pos);
32
0
    return -2;
33
0
  }
34
35
37.7k
  if (tstream->max_pos < stream->pos) {
36
    /* we seeked past the end of file. */
37
0
    ret = 0;
38
37.7k
  } else {
39
    /* copy data to a buffer in somewhat random place. this could
40
       help catch bugs. */
41
37.7k
    new_skip_diff = i_rand_limit(128);
42
37.7k
    stream->skip = (stream->skip - tstream->skip_diff) +
43
37.7k
      new_skip_diff;
44
37.7k
    stream->pos = (stream->pos - tstream->skip_diff) +
45
37.7k
      new_skip_diff;
46
37.7k
    tstream->max_pos = (tstream->max_pos - tstream->skip_diff) +
47
37.7k
      new_skip_diff;
48
37.7k
    tstream->skip_diff = new_skip_diff;
49
50
37.7k
    cur_max = tstream->max_pos;
51
37.7k
    if (stream->max_buffer_size < SIZE_MAX - stream->skip &&
52
37.7k
        cur_max > stream->skip + stream->max_buffer_size)
53
0
      cur_max = stream->skip + stream->max_buffer_size;
54
55
    /* Reallocate the memory area if needed. Use exactly correct
56
       buffer size so valgrind can catch read overflows. If a
57
       correctly sized memarea already exists, use it only if
58
       its refcount is 1. Otherwise with refcount>1 we could be
59
       moving data within an existing memarea, which breaks
60
       snapshots. */
61
37.7k
    if (cur_max > 0 && (stream->buffer_size != cur_max ||
62
37.7k
            stream->memarea == NULL ||
63
37.7k
            memarea_get_refcount(stream->memarea) > 1)) {
64
37.6k
      void *old_w_buffer = stream->w_buffer;
65
37.6k
      stream->w_buffer = i_malloc(cur_max);
66
37.6k
      if (stream->buffer_size != 0) {
67
28.6k
        memcpy(stream->w_buffer, old_w_buffer,
68
28.6k
               I_MIN(stream->buffer_size, cur_max));
69
28.6k
      }
70
37.6k
      stream->buffer = stream->w_buffer;
71
37.6k
      stream->buffer_size = cur_max;
72
73
37.6k
      if (stream->memarea != NULL)
74
37.6k
        memarea_unref(&stream->memarea);
75
37.6k
      stream->memarea = memarea_init(stream->w_buffer,
76
37.6k
                   stream->buffer_size,
77
37.6k
                   test_buffer_free,
78
37.6k
                   stream->w_buffer);
79
37.6k
    }
80
37.7k
    ssize_t size = cur_max - new_skip_diff;
81
37.7k
    if (size > 0)
82
37.7k
      memcpy(stream->w_buffer + new_skip_diff,
83
37.7k
        tstream->orig_buffer, (size_t)size);
84
85
37.7k
    ret = cur_max - stream->pos;
86
37.7k
    stream->pos = cur_max;
87
37.7k
  }
88
89
37.7k
  if (ret > 0)
90
9.01k
    return ret;
91
28.6k
  else if (!tstream->allow_eof ||
92
28.6k
     stream->pos - tstream->skip_diff < (uoff_t)stream->statbuf.st_size)
93
0
    return 0;
94
28.6k
  else {
95
28.6k
    stream->istream.eof = TRUE;
96
28.6k
    return -1;
97
28.6k
  }
98
37.7k
}
99
100
static void test_seek(struct istream_private *stream, uoff_t v_offset,
101
          bool mark ATTR_UNUSED)
102
0
{
103
0
  struct test_istream *tstream = (struct test_istream *)stream;
104
105
0
  stream->istream.v_offset = v_offset;
106
0
  stream->skip = v_offset + tstream->skip_diff;
107
0
  stream->pos = stream->skip;
108
0
}
109
110
struct istream *test_istream_create_data(const void *data, size_t size)
111
9.01k
{
112
9.01k
  struct test_istream *tstream;
113
114
9.01k
  tstream = i_new(struct test_istream, 1);
115
9.01k
  tstream->orig_buffer = data;
116
117
9.01k
  tstream->istream.read = test_read;
118
9.01k
  tstream->istream.seek = test_seek;
119
120
9.01k
  tstream->istream.istream.blocking = FALSE;
121
9.01k
  tstream->istream.istream.seekable = TRUE;
122
9.01k
  i_stream_create(&tstream->istream, NULL, -1, 0);
123
9.01k
  tstream->istream.statbuf.st_size = tstream->max_pos = size;
124
9.01k
  tstream->allow_eof = TRUE;
125
9.01k
  tstream->istream.max_buffer_size = SIZE_MAX;
126
9.01k
  return &tstream->istream.istream;
127
9.01k
}
128
129
struct istream *test_istream_create(const char *data)
130
0
{
131
0
  return test_istream_create_data(data, strlen(data));
132
0
}
133
134
static struct test_istream *test_istream_find(struct istream *input)
135
0
{
136
0
  struct istream *in;
137
138
0
  i_assert(input != NULL);
139
140
0
  for (in = input; in != NULL; in = in->real_stream->parent) {
141
0
    if (in->real_stream->read == test_read)
142
0
      return (struct test_istream *)in->real_stream;
143
0
  }
144
0
  i_panic("%s isn't test-istream", i_stream_get_name(input));
145
0
}
146
147
void test_istream_set_allow_eof(struct istream *input, bool allow)
148
0
{
149
0
  struct test_istream *tstream = test_istream_find(input);
150
151
0
  tstream->allow_eof = allow;
152
0
}
153
154
void test_istream_set_max_buffer_size(struct istream *input, size_t size)
155
0
{
156
0
  struct test_istream *tstream = test_istream_find(input);
157
158
0
  tstream->istream.max_buffer_size = size;
159
0
}
160
161
void test_istream_set_size(struct istream *input, uoff_t size)
162
0
{
163
0
  struct test_istream *tstream = test_istream_find(input);
164
165
0
  if (size > (uoff_t)tstream->istream.statbuf.st_size)
166
0
    size = (uoff_t)tstream->istream.statbuf.st_size;
167
0
  tstream->max_pos = size + tstream->skip_diff;
168
0
}