Coverage Report

Created: 2025-03-18 06:55

/src/gnutls/lib/iov.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2019 Red Hat, Inc.
3
 *
4
 * Author: Daiki Ueno
5
 *
6
 * This file is part of GnuTLS.
7
 *
8
 * The GnuTLS is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public License
10
 * as published by the Free Software Foundation; either version 2.1 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "gnutls_int.h"
24
#include "iov.h"
25
26
/**
27
 * _gnutls_iov_iter_init:
28
 * @iter: the iterator
29
 * @iov: the data buffers
30
 * @iov_count: the number of data buffers
31
 * @block_size: block size to iterate
32
 *
33
 * Initialize the iterator.
34
 *
35
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
36
 *   an error code is returned
37
 */
38
int _gnutls_iov_iter_init(struct iov_iter_st *iter, const giovec_t *iov,
39
        size_t iov_count, size_t block_size)
40
0
{
41
0
  if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE))
42
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
43
44
0
  iter->iov = iov;
45
0
  iter->iov_count = iov_count;
46
0
  iter->iov_index = 0;
47
0
  iter->iov_offset = 0;
48
0
  iter->block_size = block_size;
49
0
  iter->block_offset = 0;
50
0
  return 0;
51
0
}
52
53
/**
54
 * _gnutls_iov_iter_next:
55
 * @iter: the iterator
56
 * @data: the return location of extracted data
57
 *
58
 * Retrieve block(s) pointed by @iter and advance it to the next
59
 * position.  It returns the number of bytes in @data.  At the end of
60
 * iteration, 0 is returned.
61
 *
62
 * If the data stored in @iter is not multiple of the block size, the
63
 * remaining data is stored in the "block" field of @iter with the
64
 * size stored in the "block_offset" field.
65
 *
66
 * Returns: On success, a value greater than or equal to zero is
67
 *   returned, otherwise a negative error code is returned
68
 */
69
ssize_t _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data)
70
0
{
71
0
  while (iter->iov_index < iter->iov_count) {
72
0
    const giovec_t *iov = &iter->iov[iter->iov_index];
73
0
    uint8_t *p = iov->iov_base;
74
0
    size_t len = iov->iov_len;
75
0
    size_t block_left;
76
77
0
    if (!p) {
78
      // skip NULL iov entries, else we run into issues below
79
0
      iter->iov_index++;
80
0
      continue;
81
0
    }
82
83
0
    if (unlikely(len < iter->iov_offset))
84
0
      return gnutls_assert_val(
85
0
        GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
86
0
    len -= iter->iov_offset;
87
0
    p += iter->iov_offset;
88
89
    /* We have at least one full block, return a whole set
90
     * of full blocks immediately. */
91
0
    if (iter->block_offset == 0 && len >= iter->block_size) {
92
0
      if ((len % iter->block_size) == 0) {
93
0
        iter->iov_index++;
94
0
        iter->iov_offset = 0;
95
0
      } else {
96
0
        len -= (len % iter->block_size);
97
0
        iter->iov_offset += len;
98
0
      }
99
100
      /* Return the blocks. */
101
0
      *data = p;
102
0
      return len;
103
0
    }
104
105
    /* We can complete one full block to return. */
106
0
    block_left = iter->block_size - iter->block_offset;
107
0
    if (len >= block_left) {
108
0
      memcpy(iter->block + iter->block_offset, p, block_left);
109
0
      if (len == block_left) {
110
0
        iter->iov_index++;
111
0
        iter->iov_offset = 0;
112
0
      } else
113
0
        iter->iov_offset += block_left;
114
0
      iter->block_offset = 0;
115
116
      /* Return the filled block. */
117
0
      *data = iter->block;
118
0
      return iter->block_size;
119
0
    }
120
121
    /* Not enough data for a full block, store in temp
122
     * memory and continue. */
123
0
    memcpy(iter->block + iter->block_offset, p, len);
124
0
    iter->block_offset += len;
125
0
    iter->iov_index++;
126
0
    iter->iov_offset = 0;
127
0
  }
128
129
0
  if (iter->block_offset > 0) {
130
0
    size_t len = iter->block_offset;
131
132
    /* Return the incomplete block. */
133
0
    *data = iter->block;
134
0
    iter->block_offset = 0;
135
0
    return len;
136
0
  }
137
138
0
  return 0;
139
0
}
140
141
/**
142
 * _gnutls_iov_iter_sync:
143
 * @iter: the iterator
144
 * @data: data returned by _gnutls_iov_iter_next
145
 * @data_size: size of @data
146
 *
147
 * Flush the content of temp buffer (if any) to the data buffer.
148
 */
149
int _gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t *data,
150
        size_t data_size)
151
0
{
152
0
  size_t iov_index;
153
0
  size_t iov_offset;
154
155
  /* We didn't return the cached block. */
156
0
  if (data != iter->block)
157
0
    return 0;
158
159
0
  iov_index = iter->iov_index;
160
0
  iov_offset = iter->iov_offset;
161
162
  /* When syncing a cache block we walk backwards because we only have a
163
   * pointer to were the block ends in the iovec, walking backwards is
164
   * fine as we are always writing a full block, so the whole content
165
   * is written in the right places:
166
   * iovec:     |--0--|---1---|--2--|-3-|
167
   * block:     |-----------------------|
168
   * 1st write                      |---|
169
   * 2nd write                |-----
170
   * 3rd write        |-------
171
   * last write |-----
172
   */
173
0
  while (data_size > 0) {
174
0
    const giovec_t *iov;
175
0
    uint8_t *p;
176
0
    size_t to_write;
177
178
0
    while (iov_offset == 0) {
179
0
      if (unlikely(iov_index == 0))
180
0
        return gnutls_assert_val(
181
0
          GNUTLS_E_INTERNAL_ERROR);
182
183
0
      iov_index--;
184
0
      iov_offset = iter->iov[iov_index].iov_len;
185
0
    }
186
187
0
    iov = &iter->iov[iov_index];
188
0
    p = iov->iov_base;
189
0
    to_write = MIN(data_size, iov_offset);
190
191
0
    iov_offset -= to_write;
192
0
    data_size -= to_write;
193
194
0
    memcpy(p + iov_offset, &iter->block[data_size], to_write);
195
0
  }
196
197
0
  return 0;
198
0
}