Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/utils/stream.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * WinPR: Windows Portable Runtime
3
 * Stream Utils
4
 *
5
 * Copyright 2011 Vic Lee
6
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/config.h>
22
23
#include <winpr/assert.h>
24
#include <winpr/crt.h>
25
#include <winpr/stream.h>
26
27
#include "stream.h"
28
#include "../log.h"
29
30
0
#define STREAM_TAG WINPR_TAG("wStream")
31
32
#define STREAM_ASSERT(cond)                                                            \
33
0
  do                                                                                 \
34
0
  {                                                                                  \
35
0
    if (!(cond))                                                                   \
36
0
    {                                                                              \
37
0
      WLog_FATAL(STREAM_TAG, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, __func__, \
38
0
                 (size_t)__LINE__);                                              \
39
0
      winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);                           \
40
0
      abort();                                                                   \
41
0
    }                                                                              \
42
0
  } while (0)
43
44
BOOL Stream_EnsureCapacity(wStream* s, size_t size)
45
0
{
46
0
  WINPR_ASSERT(s);
47
0
  if (s->capacity < size)
48
0
  {
49
0
    size_t position = 0;
50
0
    size_t old_capacity = 0;
51
0
    size_t new_capacity = 0;
52
0
    BYTE* new_buf = NULL;
53
54
0
    old_capacity = s->capacity;
55
0
    new_capacity = old_capacity;
56
57
0
    do
58
0
    {
59
0
      new_capacity *= 2;
60
0
    } while (new_capacity < size);
61
62
0
    position = Stream_GetPosition(s);
63
64
0
    if (!s->isOwner)
65
0
    {
66
0
      new_buf = (BYTE*)malloc(new_capacity);
67
0
      CopyMemory(new_buf, s->buffer, s->capacity);
68
0
      s->isOwner = TRUE;
69
0
    }
70
0
    else
71
0
    {
72
0
      new_buf = (BYTE*)realloc(s->buffer, new_capacity);
73
0
    }
74
75
0
    if (!new_buf)
76
0
      return FALSE;
77
0
    s->buffer = new_buf;
78
0
    s->capacity = new_capacity;
79
0
    s->length = new_capacity;
80
0
    ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity);
81
82
0
    Stream_SetPosition(s, position);
83
0
  }
84
0
  return TRUE;
85
0
}
86
87
BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size)
88
0
{
89
0
  if (Stream_GetPosition(s) + size > Stream_Capacity(s))
90
0
    return Stream_EnsureCapacity(s, Stream_Capacity(s) + size);
91
0
  return TRUE;
92
0
}
93
94
wStream* Stream_New(BYTE* buffer, size_t size)
95
0
{
96
0
  wStream* s = NULL;
97
98
0
  if (!buffer && !size)
99
0
    return NULL;
100
101
0
  s = calloc(1, sizeof(wStream));
102
0
  if (!s)
103
0
    return NULL;
104
105
0
  if (buffer)
106
0
    s->buffer = buffer;
107
0
  else
108
0
    s->buffer = (BYTE*)malloc(size);
109
110
0
  if (!s->buffer)
111
0
  {
112
0
    free(s);
113
0
    return NULL;
114
0
  }
115
116
0
  s->pointer = s->buffer;
117
0
  s->capacity = size;
118
0
  s->length = size;
119
120
0
  s->pool = NULL;
121
0
  s->count = 1;
122
0
  s->isAllocatedStream = TRUE;
123
0
  s->isOwner = TRUE;
124
0
  return s;
125
0
}
126
127
wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size)
128
0
{
129
0
  union
130
0
  {
131
0
    BYTE* b;
132
0
    const BYTE* cb;
133
0
  } cnv;
134
135
0
  cnv.cb = buffer;
136
0
  return Stream_StaticInit(s, cnv.b, size);
137
0
}
138
139
wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size)
140
0
{
141
0
  const wStream empty = { 0 };
142
143
0
  WINPR_ASSERT(s);
144
0
  WINPR_ASSERT(buffer);
145
146
0
  *s = empty;
147
0
  s->buffer = s->pointer = buffer;
148
0
  s->capacity = s->length = size;
149
0
  s->pool = NULL;
150
0
  s->count = 1;
151
0
  s->isAllocatedStream = FALSE;
152
0
  s->isOwner = FALSE;
153
0
  return s;
154
0
}
155
156
void Stream_EnsureValidity(wStream* s)
157
0
{
158
0
  size_t cur = 0;
159
160
0
  STREAM_ASSERT(s);
161
0
  STREAM_ASSERT(s->pointer >= s->buffer);
162
163
0
  cur = (size_t)(s->pointer - s->buffer);
164
0
  STREAM_ASSERT(cur <= s->capacity);
165
0
  STREAM_ASSERT(s->length <= s->capacity);
166
0
}
167
168
void Stream_Free(wStream* s, BOOL bFreeBuffer)
169
0
{
170
0
  if (s)
171
0
  {
172
0
    Stream_EnsureValidity(s);
173
0
    if (bFreeBuffer && s->isOwner)
174
0
      free(s->buffer);
175
176
0
    if (s->isAllocatedStream)
177
0
      free(s);
178
0
  }
179
0
}
180
181
BOOL Stream_SetLength(wStream* _s, size_t _l)
182
0
{
183
0
  if ((_l) > Stream_Capacity(_s))
184
0
  {
185
0
    _s->length = 0;
186
0
    return FALSE;
187
0
  }
188
0
  _s->length = _l;
189
0
  return TRUE;
190
0
}
191
192
BOOL Stream_SetPosition(wStream* _s, size_t _p)
193
0
{
194
0
  if ((_p) > Stream_Capacity(_s))
195
0
  {
196
0
    _s->pointer = _s->buffer;
197
0
    return FALSE;
198
0
  }
199
0
  _s->pointer = _s->buffer + (_p);
200
0
  return TRUE;
201
0
}
202
203
void Stream_SealLength(wStream* _s)
204
0
{
205
0
  size_t cur = 0;
206
0
  WINPR_ASSERT(_s);
207
0
  WINPR_ASSERT(_s->buffer <= _s->pointer);
208
0
  cur = (size_t)(_s->pointer - _s->buffer);
209
0
  WINPR_ASSERT(cur <= _s->capacity);
210
0
  if (cur <= _s->capacity)
211
0
    _s->length = cur;
212
0
  else
213
0
  {
214
0
    WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
215
0
    winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
216
0
    _s->length = 0;
217
0
  }
218
0
}
219
220
#if defined(WITH_WINPR_DEPRECATED)
221
BOOL Stream_SetPointer(wStream* _s, BYTE* _p)
222
{
223
  WINPR_ASSERT(_s);
224
  if (!_p || (_s->buffer > _p) || (_s->buffer + _s->capacity < _p))
225
  {
226
    _s->pointer = _s->buffer;
227
    return FALSE;
228
  }
229
  _s->pointer = _p;
230
  return TRUE;
231
}
232
233
BOOL Stream_SetBuffer(wStream* _s, BYTE* _b)
234
{
235
  WINPR_ASSERT(_s);
236
  WINPR_ASSERT(_b);
237
238
  _s->buffer = _b;
239
  _s->pointer = _b;
240
  return _s->buffer != NULL;
241
}
242
243
void Stream_SetCapacity(wStream* _s, size_t _c)
244
{
245
  WINPR_ASSERT(_s);
246
  _s->capacity = _c;
247
}
248
249
#endif
250
251
size_t Stream_GetRemainingCapacity(const wStream* _s)
252
0
{
253
0
  size_t cur = 0;
254
0
  WINPR_ASSERT(_s);
255
0
  WINPR_ASSERT(_s->buffer <= _s->pointer);
256
0
  cur = (size_t)(_s->pointer - _s->buffer);
257
0
  WINPR_ASSERT(cur <= _s->capacity);
258
0
  if (cur > _s->capacity)
259
0
  {
260
0
    WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
261
0
    winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
262
0
    return 0;
263
0
  }
264
0
  return (_s->capacity - cur);
265
0
}
266
267
size_t Stream_GetRemainingLength(const wStream* _s)
268
0
{
269
0
  size_t cur = 0;
270
0
  WINPR_ASSERT(_s);
271
0
  WINPR_ASSERT(_s->buffer <= _s->pointer);
272
0
  WINPR_ASSERT(_s->length <= _s->capacity);
273
0
  cur = (size_t)(_s->pointer - _s->buffer);
274
0
  WINPR_ASSERT(cur <= _s->length);
275
0
  if (cur > _s->length)
276
0
  {
277
0
    WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was read out of bounds");
278
0
    winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
279
0
    return 0;
280
0
  }
281
0
  return (_s->length - cur);
282
0
}
283
284
BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t length)
285
0
{
286
0
  WINPR_ASSERT(s);
287
0
  WINPR_ASSERT(src || (length == 0));
288
0
  if (!s || !src)
289
0
    return FALSE;
290
291
0
  if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, (s), length, sizeof(WCHAR)))
292
0
    return FALSE;
293
294
0
  for (size_t x = 0; x < length; x++)
295
0
    Stream_Write_UINT16(s, src[x]);
296
297
0
  return TRUE;
298
0
}
299
300
BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t length)
301
0
{
302
0
  WINPR_ASSERT(s);
303
0
  WINPR_ASSERT(dst);
304
305
0
  if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR)))
306
0
    return FALSE;
307
308
0
  for (size_t x = 0; x < length; x++)
309
0
    Stream_Read_UINT16(s, dst[x]);
310
311
0
  return TRUE;
312
0
}
313
314
BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
315
                                          size_t size, const char* fmt, ...)
316
0
{
317
0
  WINPR_ASSERT(size != 0);
318
0
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
319
320
0
  if (actual < nmemb)
321
0
  {
322
0
    va_list args;
323
324
0
    va_start(args, fmt);
325
0
    Stream_CheckAndLogRequiredCapacityExVa(tag, level, s, nmemb, size, fmt, args);
326
0
    va_end(args);
327
328
0
    return FALSE;
329
0
  }
330
0
  return TRUE;
331
0
}
332
333
BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
334
                                            size_t size, const char* fmt, va_list args)
335
0
{
336
0
  WINPR_ASSERT(size != 0);
337
0
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
338
339
0
  if (actual < nmemb)
340
0
    return Stream_CheckAndLogRequiredCapacityWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
341
0
                                                      args);
342
0
  return TRUE;
343
0
}
344
345
WINPR_ATTR_FORMAT_ARG(6, 0)
346
BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
347
                                                size_t size, WINPR_FORMAT_ARG const char* fmt,
348
                                                va_list args)
349
0
{
350
351
0
  WINPR_ASSERT(size != 0);
352
0
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
353
354
0
  if (actual < nmemb)
355
0
  {
356
0
    char prefix[1024] = { 0 };
357
358
0
    (void)vsnprintf(prefix, sizeof(prefix), fmt, args);
359
360
0
    WLog_Print(log, level,
361
0
               "[%s] invalid remaining capacity, got %" PRIuz ", require at least %" PRIu64
362
0
               " [element size=%" PRIuz "]",
363
0
               prefix, actual, nmemb, size);
364
0
    winpr_log_backtrace_ex(log, level, 20);
365
0
    return FALSE;
366
0
  }
367
0
  return TRUE;
368
0
}
369
370
WINPR_ATTR_FORMAT_ARG(6, 7)
371
BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
372
                                              size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
373
0
{
374
375
0
  WINPR_ASSERT(size != 0);
376
0
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
377
378
0
  if (actual < nmemb)
379
0
  {
380
0
    va_list args;
381
382
0
    va_start(args, fmt);
383
0
    Stream_CheckAndLogRequiredCapacityWLogExVa(log, level, s, nmemb, size, fmt, args);
384
0
    va_end(args);
385
386
0
    return FALSE;
387
0
  }
388
0
  return TRUE;
389
0
}
390
391
WINPR_ATTR_FORMAT_ARG(6, 7)
392
BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
393
                                        size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
394
0
{
395
0
  WINPR_ASSERT(size > 0);
396
0
  const size_t actual = Stream_GetRemainingLength(s) / size;
397
398
0
  if (actual < nmemb)
399
0
  {
400
0
    va_list args;
401
402
0
    va_start(args, fmt);
403
0
    Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args);
404
0
    va_end(args);
405
406
0
    return FALSE;
407
0
  }
408
0
  return TRUE;
409
0
}
410
411
BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
412
                                          size_t size, const char* fmt, va_list args)
413
0
{
414
0
  WINPR_ASSERT(size > 0);
415
0
  const size_t actual = Stream_GetRemainingLength(s) / size;
416
417
0
  if (actual < nmemb)
418
0
    return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
419
0
                                                    args);
420
0
  return TRUE;
421
0
}
422
423
BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
424
                                            size_t size, const char* fmt, ...)
425
0
{
426
0
  WINPR_ASSERT(size > 0);
427
0
  const size_t actual = Stream_GetRemainingLength(s) / size;
428
429
0
  if (actual < nmemb)
430
0
  {
431
0
    va_list args;
432
433
0
    va_start(args, fmt);
434
0
    Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args);
435
0
    va_end(args);
436
437
0
    return FALSE;
438
0
  }
439
0
  return TRUE;
440
0
}
441
442
WINPR_ATTR_FORMAT_ARG(6, 0)
443
BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
444
                                              size_t size, WINPR_FORMAT_ARG const char* fmt,
445
                                              va_list args)
446
0
{
447
0
  WINPR_ASSERT(size > 0);
448
0
  const size_t actual = Stream_GetRemainingLength(s) / size;
449
450
0
  if (actual < nmemb)
451
0
  {
452
0
    char prefix[1024] = { 0 };
453
454
0
    (void)vsnprintf(prefix, sizeof(prefix), fmt, args);
455
456
0
    WLog_Print(log, level,
457
0
               "[%s] invalid length, got %" PRIuz ", require at least %" PRIuz
458
0
               " [element size=%" PRIuz "]",
459
0
               prefix, actual, nmemb, size);
460
0
    winpr_log_backtrace_ex(log, level, 20);
461
0
    return FALSE;
462
0
  }
463
0
  return TRUE;
464
0
}
465
466
SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t wcharLength, const char* src,
467
                                            size_t length, BOOL fill)
468
0
{
469
0
  SSIZE_T rc = 0;
470
0
  WCHAR* str = Stream_PointerAs(s, WCHAR);
471
472
0
  if (length != 0)
473
0
  {
474
0
    if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, wcharLength, sizeof(WCHAR)))
475
0
      return -1;
476
477
0
    rc = ConvertUtf8NToWChar(src, length, str, wcharLength);
478
0
    if (rc < 0)
479
0
      return -1;
480
481
0
    Stream_Seek(s, (size_t)rc * sizeof(WCHAR));
482
0
  }
483
484
0
  if (fill)
485
0
    Stream_Zero(s, (wcharLength - (size_t)rc) * sizeof(WCHAR));
486
0
  return rc;
487
0
}
488
489
char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t wcharLength, size_t* pUtfCharLength)
490
0
{
491
0
  const WCHAR* str = Stream_ConstPointer(s);
492
0
  if (wcharLength > SIZE_MAX / sizeof(WCHAR))
493
0
    return NULL;
494
495
0
  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
496
0
    return NULL;
497
498
0
  Stream_Seek(s, wcharLength * sizeof(WCHAR));
499
0
  return ConvertWCharNToUtf8Alloc(str, wcharLength, pUtfCharLength);
500
0
}
501
502
SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer,
503
                                                size_t utfBufferCharLength)
504
0
{
505
0
  const WCHAR* ptr = Stream_ConstPointer(s);
506
0
  if (wcharLength > SIZE_MAX / sizeof(WCHAR))
507
0
    return -1;
508
509
0
  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
510
0
    return -1;
511
512
0
  Stream_Seek(s, wcharLength * sizeof(WCHAR));
513
0
  return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength);
514
0
}
515
516
BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt)
517
0
{
518
0
  if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")",
519
0
                                          fkt, file, line))
520
0
    return FALSE;
521
522
0
  Stream_Seek(s, size);
523
0
  return TRUE;
524
0
}