Skip to content

Commit 123424f

Browse files
authored
Fix memory leak on invalid blocksize in pcre2_deserialize_decode (PCRE2Project#888)
1 parent 5a4faf1 commit 123424f

File tree

2 files changed

+136
-13
lines changed

2 files changed

+136
-13
lines changed

src/pcre2_serialize.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ const pcre2_memctl *memctl = (gcontext != NULL) ?
166166
&gcontext->memctl : &PRIV(default_compile_context).memctl;
167167

168168
const uint8_t *src_bytes;
169-
pcre2_real_code *dst_re;
169+
pcre2_real_code *dst_re = NULL;
170170
uint8_t *tables;
171171
int32_t i, j;
172+
int32_t error;
172173

173174
/* Sanity checks. */
174175

@@ -206,21 +207,19 @@ for (i = 0; i < number_of_codes; i++)
206207
memcpy(&blocksize, src_bytes + offsetof(pcre2_real_code, blocksize),
207208
sizeof(CODE_BLOCKSIZE_TYPE));
208209
if (blocksize <= sizeof(pcre2_real_code))
209-
return PCRE2_ERROR_BADSERIALIZEDDATA;
210+
{
211+
error = PCRE2_ERROR_BADSERIALIZEDDATA;
212+
goto cleanup;
213+
}
210214

211215
/* The allocator provided by gcontext replaces the original one. */
212216

213217
dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize,
214218
(pcre2_memctl *)gcontext);
215219
if (dst_re == NULL)
216220
{
217-
memctl->free(tables, memctl->memory_data);
218-
for (j = 0; j < i; j++)
219-
{
220-
memctl->free(codes[j], memctl->memory_data);
221-
codes[j] = NULL;
222-
}
223-
return PCRE2_ERROR_NOMEMORY;
221+
error = PCRE2_ERROR_NOMEMORY;
222+
goto cleanup;
224223
}
225224

226225
/* The new allocator must be preserved. */
@@ -230,10 +229,10 @@ for (i = 0; i < number_of_codes; i++)
230229
if (dst_re->magic_number != MAGIC_NUMBER ||
231230
dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 ||
232231
dst_re->name_count > MAX_NAME_COUNT)
233-
{
234-
memctl->free(dst_re, memctl->memory_data);
235-
return PCRE2_ERROR_BADSERIALIZEDDATA;
236-
}
232+
{
233+
error = PCRE2_ERROR_BADSERIALIZEDDATA;
234+
goto cleanup;
235+
}
237236

238237
/* At the moment only one table is supported. */
239238

@@ -242,10 +241,22 @@ for (i = 0; i < number_of_codes; i++)
242241
dst_re->flags |= PCRE2_DEREF_TABLES;
243242

244243
codes[i] = dst_re;
244+
dst_re = NULL;
245245
src_bytes += blocksize;
246246
}
247247

248248
return number_of_codes;
249+
250+
cleanup:
251+
if (dst_re != NULL)
252+
memctl->free(dst_re, memctl->memory_data);
253+
memctl->free(tables, memctl->memory_data);
254+
for (j = 0; j < i; j++)
255+
{
256+
memctl->free(codes[j], memctl->memory_data);
257+
codes[j] = NULL;
258+
}
259+
return error;
249260
}
250261

251262

src/pcre2test_inc.h

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6297,6 +6297,118 @@ rc = pcre2_substitute(subs_other_code, substitute_subject,
62976297
NULL, 0, replace_buf, &sizeval);
62986298
ASSERT(rc == PCRE2_ERROR_DIFFSUBSPATTERN, "pcre2_substitute(pattern)");
62996299

6300+
/* -------------- pcre2_serialize_decode: three goto cleanup branches -------- */
6301+
6302+
{
6303+
pcre2_code *serialize_code = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED,
6304+
0, &errorcode, &erroroffset, NULL);
6305+
uint8_t *serialized_bytes = NULL;
6306+
PCRE2_SIZE serialized_size = 0;
6307+
pcre2_code *decode_codes[1] = { NULL };
6308+
6309+
ASSERT(serialize_code != NULL, "serialize setup");
6310+
rc = pcre2_serialize_encode((const pcre2_code **)&serialize_code, 1,
6311+
&serialized_bytes, &serialized_size, NULL);
6312+
pcre2_code_free(serialize_code);
6313+
ASSERT(rc == 1 && serialized_bytes != NULL, "serialize setup");
6314+
6315+
/* goto 1: blocksize <= sizeof(pcre2_real_code) */
6316+
{
6317+
size_t blocksize_offset = sizeof(pcre2_serialized_data) + TABLES_LENGTH +
6318+
offsetof(pcre2_real_code, blocksize);
6319+
uint8_t saved_blocksize[sizeof(PCRE2_SIZE)];
6320+
memcpy(saved_blocksize, serialized_bytes + blocksize_offset,
6321+
sizeof(saved_blocksize));
6322+
memset(serialized_bytes + blocksize_offset, 0, sizeof(PCRE2_SIZE));
6323+
6324+
rc = pcre2_serialize_decode(decode_codes, 1, serialized_bytes, NULL);
6325+
ASSERT(rc == PCRE2_ERROR_BADSERIALIZEDDATA &&
6326+
decode_codes[0] == NULL, "pcre2_serialize_decode(bad blocksize)");
6327+
6328+
memcpy(serialized_bytes + blocksize_offset, saved_blocksize,
6329+
sizeof(saved_blocksize));
6330+
}
6331+
6332+
/* goto 2: dst_re malloc failure */
6333+
mallocs_until_failure = 2;
6334+
{
6335+
pcre2_general_context *serialize_test_context =
6336+
pcre2_general_context_create(&my_malloc, &my_free, NULL);
6337+
ASSERT(serialize_test_context != NULL, "general_context for serialize test");
6338+
rc = pcre2_serialize_decode(decode_codes, 1, serialized_bytes,
6339+
serialize_test_context);
6340+
ASSERT(rc == PCRE2_ERROR_NOMEMORY && decode_codes[0] == NULL,
6341+
"pcre2_serialize_decode(malloc failure)");
6342+
mallocs_until_failure = INT_MAX;
6343+
pcre2_general_context_free(serialize_test_context);
6344+
}
6345+
6346+
/* goto 3: magic_number / name_entry_size / name_count validation */
6347+
{
6348+
size_t off = sizeof(pcre2_serialized_data) + TABLES_LENGTH +
6349+
offsetof(pcre2_real_code, magic_number);
6350+
uint8_t saved[4];
6351+
memcpy(saved, serialized_bytes + off, 4);
6352+
memset(serialized_bytes + off, 0, 4);
6353+
6354+
decode_codes[0] = NULL;
6355+
rc = pcre2_serialize_decode(decode_codes, 1, serialized_bytes, NULL);
6356+
memcpy(serialized_bytes + off, saved, 4);
6357+
ASSERT(rc == PCRE2_ERROR_BADSERIALIZEDDATA &&
6358+
decode_codes[0] == NULL, "pcre2_serialize_decode(goto 3)");
6359+
}
6360+
6361+
/* Regression: avoid stale dst_re double free on later iteration failure. */
6362+
{
6363+
pcre2_code *multi_codes[2];
6364+
pcre2_code *multi_decode_codes[2] = { NULL, NULL };
6365+
uint8_t *multi_serialized_bytes = NULL;
6366+
PCRE2_SIZE multi_serialized_size = 0;
6367+
CODE_BLOCKSIZE_TYPE first_blocksize;
6368+
size_t first_blocksize_offset = sizeof(pcre2_serialized_data) +
6369+
TABLES_LENGTH + offsetof(pcre2_real_code, blocksize);
6370+
size_t second_blocksize_offset;
6371+
uint8_t saved_second_blocksize[sizeof(CODE_BLOCKSIZE_TYPE)];
6372+
6373+
multi_codes[0] = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0,
6374+
&errorcode, &erroroffset, NULL);
6375+
multi_codes[1] = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0,
6376+
&errorcode, &erroroffset, NULL);
6377+
ASSERT(multi_codes[0] != NULL && multi_codes[1] != NULL,
6378+
"serialize setup (multi-code compile)");
6379+
6380+
rc = pcre2_serialize_encode((const pcre2_code **)multi_codes, 2,
6381+
&multi_serialized_bytes, &multi_serialized_size, NULL);
6382+
pcre2_code_free(multi_codes[0]);
6383+
pcre2_code_free(multi_codes[1]);
6384+
ASSERT(rc == 2 && multi_serialized_bytes != NULL,
6385+
"serialize setup (multi-code encode)");
6386+
6387+
memcpy(&first_blocksize, multi_serialized_bytes + first_blocksize_offset,
6388+
sizeof(first_blocksize));
6389+
second_blocksize_offset = sizeof(pcre2_serialized_data) + TABLES_LENGTH +
6390+
first_blocksize + offsetof(pcre2_real_code, blocksize);
6391+
6392+
memcpy(saved_second_blocksize,
6393+
multi_serialized_bytes + second_blocksize_offset,
6394+
sizeof(saved_second_blocksize));
6395+
memset(multi_serialized_bytes + second_blocksize_offset, 0,
6396+
sizeof(saved_second_blocksize));
6397+
6398+
rc = pcre2_serialize_decode(multi_decode_codes, 2, multi_serialized_bytes,
6399+
NULL);
6400+
memcpy(multi_serialized_bytes + second_blocksize_offset,
6401+
saved_second_blocksize, sizeof(saved_second_blocksize));
6402+
ASSERT(rc == PCRE2_ERROR_BADSERIALIZEDDATA &&
6403+
multi_decode_codes[0] == NULL && multi_decode_codes[1] == NULL,
6404+
"pcre2_serialize_decode(regression stale dst_re)");
6405+
6406+
pcre2_serialize_free(multi_serialized_bytes);
6407+
}
6408+
6409+
pcre2_serialize_free(serialized_bytes);
6410+
}
6411+
63006412
/* ------------------------------------------------------------------------- */
63016413

63026414
#undef ASSERT

0 commit comments

Comments
 (0)