Coverage Report

Created: 2023-03-26 07:33

/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
39
_gnutls_iov_iter_init(struct iov_iter_st *iter,
40
          const giovec_t * iov, size_t iov_count, size_t block_size)
41
0
{
42
0
  if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE))
43
0
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
44
45
0
  iter->iov = iov;
46
0
  iter->iov_count = iov_count;
47
0
  iter->iov_index = 0;
48
0
  iter->iov_offset = 0;
49
0
  iter->block_size = block_size;
50
0
  iter->block_offset = 0;
51
0
  return 0;
52
0
}
53
54
/**
55
 * _gnutls_iov_iter_next:
56
 * @iter: the iterator
57
 * @data: the return location of extracted data
58
 *
59
 * Retrieve block(s) pointed by @iter and advance it to the next
60
 * position.  It returns the number of bytes in @data.  At the end of
61
 * iteration, 0 is returned.
62
 *
63
 * If the data stored in @iter is not multiple of the block size, the
64
 * remaining data is stored in the "block" field of @iter with the
65
 * size stored in the "block_offset" field.
66
 *
67
 * Returns: On success, a value greater than or equal to zero is
68
 *   returned, otherwise a negative error code is returned
69
 */
70
ssize_t _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t ** data)
71
0
{
72
0
  while (iter->iov_index < iter->iov_count) {
73
0
    const giovec_t *iov = &iter->iov[iter->iov_index];
74
0
    uint8_t *p = iov->iov_base;
75
0
    size_t len = iov->iov_len;
76
0
    size_t block_left;
77
78
0
    if (!p) {
79
      // skip NULL iov entries, else we run into issues below
80
0
      iter->iov_index++;
81
0
      continue;
82
0
    }
83
84
0
    if (unlikely(len < iter->iov_offset))
85
0
      return
86
0
          gnutls_assert_val
87
0
          (GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
88
0
    len -= iter->iov_offset;
89
0
    p += iter->iov_offset;
90
91
    /* We have at least one full block, return a whole set
92
     * of full blocks immediately. */
93
0
    if (iter->block_offset == 0 && len >= iter->block_size) {
94
0
      if ((len % iter->block_size) == 0) {
95
0
        iter->iov_index++;
96
0
        iter->iov_offset = 0;
97
0
      } else {
98
0
        len -= (len % iter->block_size);
99
0
        iter->iov_offset += len;
100
0
      }
101
102
      /* Return the blocks. */
103
0
      *data = p;
104
0
      return len;
105
0
    }
106
107
    /* We can complete one full block to return. */
108
0
    block_left = iter->block_size - iter->block_offset;
109
0
    if (len >= block_left) {
110
0
      memcpy(iter->block + iter->block_offset, p, block_left);
111
0
      if (len == block_left) {
112
0
        iter->iov_index++;
113
0
        iter->iov_offset = 0;
114
0
      } else
115
0
        iter->iov_offset += block_left;
116
0
      iter->block_offset = 0;
117
118
      /* Return the filled block. */
119
0
      *data = iter->block;
120
0
      return iter->block_size;
121
0
    }
122
123
    /* Not enough data for a full block, store in temp
124
     * memory and continue. */
125
0
    memcpy(iter->block + iter->block_offset, p, len);
126
0
    iter->block_offset += len;
127
0
    iter->iov_index++;
128
0
    iter->iov_offset = 0;
129
0
  }
130
131
0
  if (iter->block_offset > 0) {
132
0
    size_t len = iter->block_offset;
133
134
    /* Return the incomplete block. */
135
0
    *data = iter->block;
136
0
    iter->block_offset = 0;
137
0
    return len;
138
0
  }
139
140
0
  return 0;
141
0
}
142
143
/**
144
 * _gnutls_iov_iter_sync:
145
 * @iter: the iterator
146
 * @data: data returned by _gnutls_iov_iter_next
147
 * @data_size: size of @data
148
 *
149
 * Flush the content of temp buffer (if any) to the data buffer.
150
 */
151
int
152
_gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t * data,
153
          size_t data_size)
154
0
{
155
0
  size_t iov_index;
156
0
  size_t iov_offset;
157
158
  /* We didn't return the cached block. */
159
0
  if (data != iter->block)
160
0
    return 0;
161
162
0
  iov_index = iter->iov_index;
163
0
  iov_offset = iter->iov_offset;
164
165
  /* When syncing a cache block we walk backwards because we only have a
166
   * pointer to were the block ends in the iovec, walking backwards is
167
   * fine as we are always writing a full block, so the whole content
168
   * is written in the right places:
169
   * iovec:     |--0--|---1---|--2--|-3-|
170
   * block:     |-----------------------|
171
   * 1st write                      |---|
172
   * 2nd write                |-----
173
   * 3rd write        |-------
174
   * last write |-----
175
   */
176
0
  while (data_size > 0) {
177
0
    const giovec_t *iov;
178
0
    uint8_t *p;
179
0
    size_t to_write;
180
181
0
    while (iov_offset == 0) {
182
0
      if (unlikely(iov_index == 0))
183
0
        return
184
0
            gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
185
186
0
      iov_index--;
187
0
      iov_offset = iter->iov[iov_index].iov_len;
188
0
    }
189
190
0
    iov = &iter->iov[iov_index];
191
0
    p = iov->iov_base;
192
0
    to_write = MIN(data_size, iov_offset);
193
194
0
    iov_offset -= to_write;
195
0
    data_size -= to_write;
196
197
0
    memcpy(p + iov_offset, &iter->block[data_size], to_write);
198
0
  }
199
200
0
  return 0;
201
0
}