Coverage Report

Created: 2024-05-20 06:11

/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
123k
#define STREAM_TAG WINPR_TAG("wStream")
31
32
#define STREAM_ASSERT(cond)                                                            \
33
619k
  do                                                                                 \
34
619k
  {                                                                                  \
35
619k
    if (!(cond))                                                                   \
36
619k
    {                                                                              \
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
619k
  } while (0)
43
44
BOOL Stream_EnsureCapacity(wStream* s, size_t size)
45
3.70k
{
46
3.70k
  WINPR_ASSERT(s);
47
3.70k
  if (s->capacity < size)
48
59
  {
49
59
    size_t position = 0;
50
59
    size_t old_capacity = 0;
51
59
    size_t new_capacity = 0;
52
59
    BYTE* new_buf = NULL;
53
54
59
    old_capacity = s->capacity;
55
59
    new_capacity = old_capacity;
56
57
59
    do
58
145
    {
59
145
      new_capacity *= 2;
60
145
    } while (new_capacity < size);
61
62
59
    position = Stream_GetPosition(s);
63
64
59
    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
59
    else
71
59
    {
72
59
      new_buf = (BYTE*)realloc(s->buffer, new_capacity);
73
59
    }
74
75
59
    if (!new_buf)
76
0
      return FALSE;
77
59
    s->buffer = new_buf;
78
59
    s->capacity = new_capacity;
79
59
    s->length = new_capacity;
80
59
    ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity);
81
82
59
    Stream_SetPosition(s, position);
83
59
  }
84
3.70k
  return TRUE;
85
3.70k
}
86
87
BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size)
88
18.5k
{
89
18.5k
  if (Stream_GetPosition(s) + size > Stream_Capacity(s))
90
59
    return Stream_EnsureCapacity(s, Stream_Capacity(s) + size);
91
18.5k
  return TRUE;
92
18.5k
}
93
94
wStream* Stream_New(BYTE* buffer, size_t size)
95
104k
{
96
104k
  wStream* s = NULL;
97
98
104k
  if (!buffer && !size)
99
0
    return NULL;
100
101
104k
  s = malloc(sizeof(wStream));
102
104k
  if (!s)
103
0
    return NULL;
104
105
104k
  if (buffer)
106
0
    s->buffer = buffer;
107
104k
  else
108
104k
    s->buffer = (BYTE*)malloc(size);
109
110
104k
  if (!s->buffer)
111
0
  {
112
0
    free(s);
113
0
    return NULL;
114
0
  }
115
116
104k
  s->pointer = s->buffer;
117
104k
  s->capacity = size;
118
104k
  s->length = size;
119
120
104k
  s->pool = NULL;
121
104k
  s->count = 0;
122
104k
  s->isAllocatedStream = TRUE;
123
104k
  s->isOwner = TRUE;
124
104k
  return s;
125
104k
}
126
127
wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size)
128
89.6k
{
129
89.6k
  union
130
89.6k
  {
131
89.6k
    BYTE* b;
132
89.6k
    const BYTE* cb;
133
89.6k
  } cnv;
134
135
89.6k
  cnv.cb = buffer;
136
89.6k
  return Stream_StaticInit(s, cnv.b, size);
137
89.6k
}
138
139
wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size)
140
139k
{
141
139k
  const wStream empty = { 0 };
142
143
139k
  WINPR_ASSERT(s);
144
139k
  WINPR_ASSERT(buffer);
145
146
139k
  *s = empty;
147
139k
  s->buffer = s->pointer = buffer;
148
139k
  s->capacity = s->length = size;
149
139k
  s->pool = NULL;
150
139k
  s->count = 0;
151
139k
  s->isAllocatedStream = FALSE;
152
139k
  s->isOwner = FALSE;
153
139k
  return s;
154
139k
}
155
156
void Stream_EnsureValidity(wStream* s)
157
154k
{
158
154k
  size_t cur = 0;
159
160
154k
  STREAM_ASSERT(s);
161
154k
  STREAM_ASSERT(s->pointer >= s->buffer);
162
163
154k
  cur = (size_t)(s->pointer - s->buffer);
164
154k
  STREAM_ASSERT(cur <= s->capacity);
165
154k
  STREAM_ASSERT(s->length <= s->capacity);
166
154k
}
167
168
void Stream_Free(wStream* s, BOOL bFreeBuffer)
169
104k
{
170
104k
  if (s)
171
104k
  {
172
104k
    Stream_EnsureValidity(s);
173
104k
    if (bFreeBuffer && s->isOwner)
174
104k
      free(s->buffer);
175
176
104k
    if (s->isAllocatedStream)
177
104k
      free(s);
178
104k
  }
179
104k
}
180
181
BOOL Stream_SetLength(wStream* _s, size_t _l)
182
376
{
183
376
  if ((_l) > Stream_Capacity(_s))
184
0
  {
185
0
    _s->length = 0;
186
0
    return FALSE;
187
0
  }
188
376
  _s->length = _l;
189
376
  return TRUE;
190
376
}
191
192
BOOL Stream_SetPosition(wStream* _s, size_t _p)
193
30.5k
{
194
30.5k
  if ((_p) > Stream_Capacity(_s))
195
0
  {
196
0
    _s->pointer = _s->buffer;
197
0
    return FALSE;
198
0
  }
199
30.5k
  _s->pointer = _s->buffer + (_p);
200
30.5k
  return TRUE;
201
30.5k
}
202
203
void Stream_SealLength(wStream* _s)
204
19.9k
{
205
19.9k
  size_t cur = 0;
206
19.9k
  WINPR_ASSERT(_s);
207
19.9k
  WINPR_ASSERT(_s->buffer <= _s->pointer);
208
19.9k
  cur = (size_t)(_s->pointer - _s->buffer);
209
19.9k
  WINPR_ASSERT(cur <= _s->capacity);
210
19.9k
  if (cur <= _s->capacity)
211
19.9k
    _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
19.9k
}
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
8.91M
{
253
8.91M
  size_t cur = 0;
254
8.91M
  WINPR_ASSERT(_s);
255
8.91M
  WINPR_ASSERT(_s->buffer <= _s->pointer);
256
8.91M
  cur = (size_t)(_s->pointer - _s->buffer);
257
8.91M
  WINPR_ASSERT(cur <= _s->capacity);
258
8.91M
  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
8.91M
  return (_s->capacity - cur);
265
8.91M
}
266
267
size_t Stream_GetRemainingLength(const wStream* _s)
268
16.7M
{
269
16.7M
  size_t cur = 0;
270
16.7M
  WINPR_ASSERT(_s);
271
16.7M
  WINPR_ASSERT(_s->buffer <= _s->pointer);
272
16.7M
  WINPR_ASSERT(_s->length <= _s->capacity);
273
16.7M
  cur = (size_t)(_s->pointer - _s->buffer);
274
16.7M
  WINPR_ASSERT(cur <= _s->length);
275
16.7M
  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
16.7M
  return (_s->length - cur);
282
16.7M
}
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
13.2k
{
302
13.2k
  WINPR_ASSERT(s);
303
13.2k
  WINPR_ASSERT(dst);
304
305
13.2k
  if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR)))
306
0
    return FALSE;
307
308
435k
  for (size_t x = 0; x < length; x++)
309
422k
    Stream_Read_UINT16(s, dst[x]);
310
311
13.2k
  return TRUE;
312
13.2k
}
313
314
BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
315
                                          size_t size, const char* fmt, ...)
316
6.87k
{
317
6.87k
  WINPR_ASSERT(size != 0);
318
6.87k
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
319
320
6.87k
  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
6.87k
  return TRUE;
331
6.87k
}
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
    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
3.12k
{
374
375
3.12k
  WINPR_ASSERT(size != 0);
376
3.12k
  const size_t actual = Stream_GetRemainingCapacity(s) / size;
377
378
3.12k
  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
3.12k
  return TRUE;
389
3.12k
}
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
7.65M
{
395
7.65M
  WINPR_ASSERT(size > 0);
396
7.65M
  const size_t actual = Stream_GetRemainingLength(s) / size;
397
398
7.65M
  if (actual < nmemb)
399
121k
  {
400
121k
    va_list args;
401
402
121k
    va_start(args, fmt);
403
121k
    Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args);
404
121k
    va_end(args);
405
406
121k
    return FALSE;
407
121k
  }
408
7.53M
  return TRUE;
409
7.65M
}
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
121k
{
414
121k
  WINPR_ASSERT(size > 0);
415
121k
  const size_t actual = Stream_GetRemainingLength(s) / size;
416
417
121k
  if (actual < nmemb)
418
121k
    return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
419
121k
                                                    args);
420
0
  return TRUE;
421
121k
}
422
423
BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
424
                                            size_t size, const char* fmt, ...)
425
99.0k
{
426
99.0k
  WINPR_ASSERT(size > 0);
427
99.0k
  const size_t actual = Stream_GetRemainingLength(s) / size;
428
429
99.0k
  if (actual < nmemb)
430
36.1k
  {
431
36.1k
    va_list args;
432
433
36.1k
    va_start(args, fmt);
434
36.1k
    Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args);
435
36.1k
    va_end(args);
436
437
36.1k
    return FALSE;
438
36.1k
  }
439
62.8k
  return TRUE;
440
99.0k
}
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
157k
{
447
157k
  WINPR_ASSERT(size > 0);
448
157k
  const size_t actual = Stream_GetRemainingLength(s) / size;
449
450
157k
  if (actual < nmemb)
451
157k
  {
452
157k
    char prefix[1024] = { 0 };
453
454
157k
    vsnprintf(prefix, sizeof(prefix), fmt, args);
455
456
157k
    WLog_Print(log, level,
457
157k
               "[%s] invalid length, got %" PRIuz ", require at least %" PRIuz
458
157k
               " [element size=%" PRIuz "]",
459
157k
               prefix, actual, nmemb, size);
460
157k
    winpr_log_backtrace_ex(log, level, 20);
461
157k
    return FALSE;
462
157k
  }
463
0
  return TRUE;
464
157k
}
465
466
SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t dlen, const char* src, size_t length,
467
                                            BOOL fill)
468
0
{
469
0
  WCHAR* str = Stream_PointerAs(s, WCHAR);
470
471
0
  if (length == 0)
472
0
    return 0;
473
474
0
  if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, dlen, sizeof(WCHAR)))
475
0
    return -1;
476
477
0
  SSIZE_T rc = ConvertUtf8NToWChar(src, length, str, dlen);
478
0
  if (rc < 0)
479
0
    return -1;
480
481
0
  Stream_Seek(s, (size_t)rc * sizeof(WCHAR));
482
483
0
  if (fill)
484
0
    Stream_Zero(s, (dlen - (size_t)rc) * sizeof(WCHAR));
485
0
  return rc;
486
0
}
487
488
char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t dlen, size_t* psize)
489
0
{
490
0
  const WCHAR* str = Stream_ConstPointer(s);
491
0
  if (dlen > SIZE_MAX / sizeof(WCHAR))
492
0
    return NULL;
493
494
0
  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, dlen * sizeof(WCHAR)))
495
0
    return NULL;
496
497
0
  Stream_Seek(s, dlen * sizeof(WCHAR));
498
0
  return ConvertWCharNToUtf8Alloc(str, dlen, psize);
499
0
}
500
501
SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer,
502
                                                size_t utfBufferCharLength)
503
0
{
504
0
  const WCHAR* ptr = Stream_ConstPointer(s);
505
0
  if (wcharLength > SIZE_MAX / sizeof(WCHAR))
506
0
    return -1;
507
508
0
  if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
509
0
    return -1;
510
511
0
  Stream_Seek(s, wcharLength * sizeof(WCHAR));
512
0
  return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength);
513
0
}
514
515
BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt)
516
123k
{
517
123k
  if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")",
518
123k
                                          fkt, file, line))
519
6.56k
    return FALSE;
520
521
116k
  Stream_Seek(s, size);
522
116k
  return TRUE;
523
123k
}