Coverage Report

Created: 2026-01-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/membuf.c
Line
Count
Source
1
/* membuf.c - A simple implementation of a dynamic buffer.
2
 * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
3
 * Copyright (C) 2013 Werner Koch
4
 *
5
 * This file is part of GnuPG.
6
 *
7
 * This file is free software; you can redistribute it and/or modify
8
 * it under the terms of either
9
 *
10
 *   - the GNU Lesser General Public License as published by the Free
11
 *     Software Foundation; either version 3 of the License, or (at
12
 *     your option) any later version.
13
 *
14
 * or
15
 *
16
 *   - the GNU General Public License as published by the Free
17
 *     Software Foundation; either version 2 of the License, or (at
18
 *     your option) any later version.
19
 *
20
 * or both in parallel, as here.
21
 *
22
 * This file is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
29
 */
30
31
#include <config.h>
32
#include <stdlib.h>
33
#include <errno.h>
34
#include <stdarg.h>
35
36
#include "util.h"
37
#include "membuf.h"
38
39
40
/* A simple implementation of a dynamic buffer.  Use init_membuf() to
41
   create a buffer, put_membuf to append bytes and get_membuf to
42
   release and return the buffer.  Allocation errors are detected but
43
   only returned at the final get_membuf(), this helps not to clutter
44
   the code with out of core checks.  */
45
46
void
47
init_membuf (membuf_t *mb, int initiallen)
48
0
{
49
0
  mb->len = 0;
50
0
  mb->size = initiallen;
51
0
  mb->out_of_core = 0;
52
0
  mb->buf = xtrymalloc (initiallen);
53
0
  if (!mb->buf)
54
0
    mb->out_of_core = errno;
55
0
}
56
57
/* Same as init_membuf but allocates the buffer in secure memory.  */
58
void
59
init_membuf_secure (membuf_t *mb, int initiallen)
60
0
{
61
0
  mb->len = 0;
62
0
  mb->size = initiallen;
63
0
  mb->out_of_core = 0;
64
0
  mb->buf = xtrymalloc_secure (initiallen);
65
0
  if (!mb->buf)
66
0
    mb->out_of_core = errno;
67
0
}
68
69
70
/* Shift the content of the membuf MB by AMOUNT bytes.  The next
71
   operation will then behave as if AMOUNT bytes had not been put into
72
   the buffer.  If AMOUNT is greater than the actual accumulated
73
   bytes, the membuf is basically reset to its initial state.  */
74
void
75
clear_membuf (membuf_t *mb, size_t amount)
76
0
{
77
  /* No need to clear if we are already out of core.  */
78
0
  if (mb->out_of_core)
79
0
    return;
80
0
  if (amount >= mb->len)
81
0
    mb->len = 0;
82
0
  else
83
0
    {
84
0
      mb->len -= amount;
85
0
      memmove (mb->buf, mb->buf+amount, mb->len);
86
0
    }
87
0
}
88
89
90
void
91
put_membuf (membuf_t *mb, const void *buf, size_t len)
92
0
{
93
0
  if (mb->out_of_core || !len)
94
0
    return;
95
96
0
  if (mb->len + len >= mb->size)
97
0
    {
98
0
      char *p;
99
100
0
      mb->size += len + 1024;
101
0
      p = xtryrealloc (mb->buf, mb->size);
102
0
      if (!p)
103
0
        {
104
0
          mb->out_of_core = errno ? errno : ENOMEM;
105
          /* Wipe out what we already accumulated.  This is required
106
             in case we are storing sensitive data here.  The membuf
107
             API does not provide another way to cleanup after an
108
             error. */
109
0
          wipememory (mb->buf, mb->len);
110
0
          return;
111
0
        }
112
0
      mb->buf = p;
113
0
    }
114
0
  if (buf)
115
0
    memcpy (mb->buf + mb->len, buf, len);
116
0
  else
117
0
    memset (mb->buf + mb->len, 0, len);
118
0
  mb->len += len;
119
0
}
120
121
122
/* A variant of put_membuf accepting a void * and returning a
123
   gpg_error_t (which will always return 0) to be used as a generic
124
   callback handler.  This function also allows buffer to be NULL.  */
125
gpg_error_t
126
put_membuf_cb (void *opaque, const void *buf, size_t len)
127
0
{
128
0
  membuf_t *data = opaque;
129
130
0
  if (buf)
131
0
    put_membuf (data, buf, len);
132
0
  return 0;
133
0
}
134
135
136
void
137
put_membuf_str (membuf_t *mb, const char *string)
138
0
{
139
0
  put_membuf (mb, string, strlen (string));
140
0
}
141
142
143
void
144
put_membuf_printf (membuf_t *mb, const char *format, ...)
145
0
{
146
0
  int rc;
147
0
  va_list arg_ptr;
148
0
  char *buf;
149
150
0
  va_start (arg_ptr, format);
151
0
  rc = gpgrt_vasprintf (&buf, format, arg_ptr);
152
0
  if (rc < 0)
153
0
    mb->out_of_core = errno ? errno : ENOMEM;
154
0
  va_end (arg_ptr);
155
0
  if (rc >= 0)
156
0
    {
157
0
      put_membuf (mb, buf, strlen (buf));
158
0
      xfree (buf);
159
0
    }
160
0
}
161
162
163
void *
164
get_membuf (membuf_t *mb, size_t *len)
165
0
{
166
0
  char *p;
167
168
0
  if (mb->out_of_core)
169
0
    {
170
0
      if (mb->buf)
171
0
        {
172
0
          wipememory (mb->buf, mb->len);
173
0
          xfree (mb->buf);
174
0
          mb->buf = NULL;
175
0
        }
176
0
      gpg_err_set_errno (mb->out_of_core);
177
0
      return NULL;
178
0
    }
179
180
0
  p = mb->buf;
181
0
  if (len)
182
0
    *len = mb->len;
183
0
  mb->buf = NULL;
184
0
  mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
185
0
  return p;
186
0
}
187
188
189
/* Same as get_membuf but shrinks the reallocated space to the
190
   required size.  */
191
void *
192
get_membuf_shrink (membuf_t *mb, size_t *len)
193
0
{
194
0
  void *p, *pp;
195
0
  size_t dummylen;
196
197
0
  if (!len)
198
0
    len = &dummylen;
199
200
0
  p = get_membuf (mb, len);
201
0
  if (!p)
202
0
    return NULL;
203
0
  if (*len)
204
0
    {
205
0
      pp = xtryrealloc (p, *len);
206
0
      if (pp)
207
0
        p = pp;
208
0
    }
209
210
0
  return p;
211
0
}
212
213
214
/* Peek at the membuf MB.  On success a pointer to the buffer is
215
   returned which is valid until the next operation on MB.  If LEN is
216
   not NULL the current LEN of the buffer is stored there.  On error
217
   NULL is returned and ERRNO is set.  */
218
const void *
219
peek_membuf (membuf_t *mb, size_t *len)
220
0
{
221
0
  const char *p;
222
223
0
  if (mb->out_of_core)
224
0
    {
225
0
      gpg_err_set_errno (mb->out_of_core);
226
0
      return NULL;
227
0
    }
228
229
0
  p = mb->buf;
230
0
  if (len)
231
0
    *len = mb->len;
232
0
  return p;
233
0
}
234
235
/* To assist using membuf with function returning an error, this
236
 * function sets the membuf into the error state.  */
237
void
238
set_membuf_err (membuf_t *mb, gpg_error_t err)
239
0
{
240
0
  if (!mb->out_of_core)
241
0
    {
242
0
      int myerr = gpg_err_code_to_errno (gpg_err_code (err));
243
0
      mb->out_of_core = myerr? myerr : EINVAL;
244
0
    }
245
0
}