Skip to content

Commit 3d8c2b4

Browse files
committed
Fix broken allocation logic in recently-rewritten jsonb_util.c.
reserveFromBuffer() failed to consider the possibility that it needs to more-than-double the current buffer size. Beyond that, it seems likely that we'd someday need to worry about integer overflow of the buffer length variable. Rather than reinvent the logic that's already been debugged in stringinfo.c, let's go back to using that logic. We can still have the same targeted API, but we'll rely on stringinfo.c to manage reallocation. Per report from Alexander Korotkov.
1 parent 0b92a77 commit 3d8c2b4

File tree

1 file changed

+35
-43
lines changed

1 file changed

+35
-43
lines changed

src/backend/utils/adt/jsonb_util.c

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,20 @@
3232
#define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JB_CMASK))
3333
#define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), JB_CMASK))
3434

35-
/*
36-
* convertState: a resizeable buffer used when constructing a Jsonb datum
37-
*/
38-
typedef struct
39-
{
40-
char *buffer;
41-
int len;
42-
int allocatedsz;
43-
} convertState;
44-
4535
static void fillJsonbValue(JEntry *array, int index, char *base_addr,
4636
JsonbValue *result);
4737
static int compareJsonbScalarValue(JsonbValue *a, JsonbValue *b);
4838
static int lexicalCompareJsonbStringValue(const void *a, const void *b);
4939
static Jsonb *convertToJsonb(JsonbValue *val);
50-
static void convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level);
51-
static void convertJsonbArray(convertState *buffer, JEntry *header, JsonbValue *val, int level);
52-
static void convertJsonbObject(convertState *buffer, JEntry *header, JsonbValue *val, int level);
53-
static void convertJsonbScalar(convertState *buffer, JEntry *header, JsonbValue *scalarVal);
40+
static void convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
41+
static void convertJsonbArray(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
42+
static void convertJsonbObject(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
43+
static void convertJsonbScalar(StringInfo buffer, JEntry *header, JsonbValue *scalarVal);
5444

55-
static int reserveFromBuffer(convertState *buffer, int len);
56-
static void appendToBuffer(convertState *buffer, char *data, int len);
57-
static void copyToBuffer(convertState *buffer, int offset, char *data, int len);
58-
static short padBufferToInt(convertState *buffer);
45+
static int reserveFromBuffer(StringInfo buffer, int len);
46+
static void appendToBuffer(StringInfo buffer, const char *data, int len);
47+
static void copyToBuffer(StringInfo buffer, int offset, const char *data, int len);
48+
static short padBufferToInt(StringInfo buffer);
5949

6050
static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
6151
static JsonbIterator *freeAndGetParent(JsonbIterator *it);
@@ -1175,44 +1165,46 @@ lexicalCompareJsonbStringValue(const void *a, const void *b)
11751165

11761166
/*
11771167
* Reserve 'len' bytes, at the end of the buffer, enlarging it if necessary.
1178-
* Returns the offset to the reserved area. The caller is expected to copy
1179-
* the data to the reserved area later with copyToBuffer()
1168+
* Returns the offset to the reserved area. The caller is expected to fill
1169+
* the reserved area later with copyToBuffer().
11801170
*/
11811171
static int
1182-
reserveFromBuffer(convertState *buffer, int len)
1172+
reserveFromBuffer(StringInfo buffer, int len)
11831173
{
11841174
int offset;
11851175

11861176
/* Make more room if needed */
1187-
if (buffer->len + len > buffer->allocatedsz)
1188-
{
1189-
buffer->allocatedsz *= 2;
1190-
buffer->buffer = repalloc(buffer->buffer, buffer->allocatedsz);
1191-
}
1177+
enlargeStringInfo(buffer, len);
11921178

11931179
/* remember current offset */
11941180
offset = buffer->len;
11951181

11961182
/* reserve the space */
11971183
buffer->len += len;
11981184

1185+
/*
1186+
* Keep a trailing null in place, even though it's not useful for us;
1187+
* it seems best to preserve the invariants of StringInfos.
1188+
*/
1189+
buffer->data[buffer->len] = '\0';
1190+
11991191
return offset;
12001192
}
12011193

12021194
/*
12031195
* Copy 'len' bytes to a previously reserved area in buffer.
12041196
*/
12051197
static void
1206-
copyToBuffer(convertState *buffer, int offset, char *data, int len)
1198+
copyToBuffer(StringInfo buffer, int offset, const char *data, int len)
12071199
{
1208-
memcpy(buffer->buffer + offset, data, len);
1200+
memcpy(buffer->data + offset, data, len);
12091201
}
12101202

12111203
/*
12121204
* A shorthand for reserveFromBuffer + copyToBuffer.
12131205
*/
12141206
static void
1215-
appendToBuffer(convertState *buffer, char *data, int len)
1207+
appendToBuffer(StringInfo buffer, const char *data, int len)
12161208
{
12171209
int offset;
12181210

@@ -1226,17 +1218,19 @@ appendToBuffer(convertState *buffer, char *data, int len)
12261218
* Returns the number of padding bytes appended.
12271219
*/
12281220
static short
1229-
padBufferToInt(convertState *buffer)
1221+
padBufferToInt(StringInfo buffer)
12301222
{
1231-
short padlen,
1232-
p;
1233-
int offset;
1223+
int padlen,
1224+
p,
1225+
offset;
12341226

12351227
padlen = INTALIGN(buffer->len) - buffer->len;
12361228

12371229
offset = reserveFromBuffer(buffer, padlen);
1230+
1231+
/* padlen must be small, so this is probably faster than a memset */
12381232
for (p = 0; p < padlen; p++)
1239-
buffer->buffer[offset + p] = 0;
1233+
buffer->data[offset + p] = '\0';
12401234

12411235
return padlen;
12421236
}
@@ -1247,17 +1241,15 @@ padBufferToInt(convertState *buffer)
12471241
static Jsonb *
12481242
convertToJsonb(JsonbValue *val)
12491243
{
1250-
convertState buffer;
1244+
StringInfoData buffer;
12511245
JEntry jentry;
12521246
Jsonb *res;
12531247

12541248
/* Should not already have binary representation */
12551249
Assert(val->type != jbvBinary);
12561250

12571251
/* Allocate an output buffer. It will be enlarged as needed */
1258-
buffer.buffer = palloc(128);
1259-
buffer.len = 0;
1260-
buffer.allocatedsz = 128;
1252+
initStringInfo(&buffer);
12611253

12621254
/* Make room for the varlena header */
12631255
reserveFromBuffer(&buffer, sizeof(VARHDRSZ));
@@ -1270,7 +1262,7 @@ convertToJsonb(JsonbValue *val)
12701262
* kind of value it is.
12711263
*/
12721264

1273-
res = (Jsonb *) buffer.buffer;
1265+
res = (Jsonb *) buffer.data;
12741266

12751267
SET_VARSIZE(res, buffer.len);
12761268

@@ -1289,7 +1281,7 @@ convertToJsonb(JsonbValue *val)
12891281
* for debugging purposes.
12901282
*/
12911283
static void
1292-
convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level)
1284+
convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level)
12931285
{
12941286
check_stack_depth();
12951287

@@ -1307,7 +1299,7 @@ convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int lev
13071299
}
13081300

13091301
static void
1310-
convertJsonbArray(convertState *buffer, JEntry *pheader, JsonbValue *val, int level)
1302+
convertJsonbArray(StringInfo buffer, JEntry *pheader, JsonbValue *val, int level)
13111303
{
13121304
int offset;
13131305
int metaoffset;
@@ -1366,7 +1358,7 @@ convertJsonbArray(convertState *buffer, JEntry *pheader, JsonbValue *val, int le
13661358
}
13671359

13681360
static void
1369-
convertJsonbObject(convertState *buffer, JEntry *pheader, JsonbValue *val, int level)
1361+
convertJsonbObject(StringInfo buffer, JEntry *pheader, JsonbValue *val, int level)
13701362
{
13711363
uint32 header;
13721364
int offset;
@@ -1431,7 +1423,7 @@ convertJsonbObject(convertState *buffer, JEntry *pheader, JsonbValue *val, int l
14311423
}
14321424

14331425
static void
1434-
convertJsonbScalar(convertState *buffer, JEntry *jentry, JsonbValue *scalarVal)
1426+
convertJsonbScalar(StringInfo buffer, JEntry *jentry, JsonbValue *scalarVal)
14351427
{
14361428
int numlen;
14371429
short padlen;

0 commit comments

Comments
 (0)