Re: Invalid memory alloc request size for repeat() - Mailing list pgsql-hackers

From Japin Li
Subject Re: Invalid memory alloc request size for repeat()
Date
Msg-id MEYP282MB16693D6214D353E14C29F40EB6D89@MEYP282MB1669.AUSP282.PROD.OUTLOOK.COM
Whole thread Raw
In response to Re: Invalid memory alloc request size for repeat()  (Japin Li <japinli@hotmail.com>)
Responses Re: Invalid memory alloc request size for repeat()
List pgsql-hackers
On Thu, 26 May 2022 at 09:03, Japin Li <japinli@hotmail.com> wrote:
> On Wed, 25 May 2022 at 22:50, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Japin Li <japinli@hotmail.com> writes:
>>> Today, I try to use repeat() to generate 1GB text, and it occurs invalid memory
>>> alloc request size [1].  It is a limit from palloc(), then I try to reduce it,
>>> it still complains out of memory which comes from enlargeStringInfo() [2].  The
>>> documentation about repect() [3] doesn't mentaion the limitation.
>>
>> It would probably make sense for repeat() to check this explicitly:
>>
>>     if (unlikely(pg_mul_s32_overflow(count, slen, &tlen)) ||
>> -       unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)))
>> +       unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)) ||
>> +       unlikely(!AllocSizeIsValid(tlen)))
>>         ereport(ERROR,
>>                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
>>                  errmsg("requested length too large")));
>>
>
> LGTM.  Thanks for your patch!

After some analysis, I found it might not easy to solve this.

For example,

```
postgres=# CREATE TABLE myrepeat AS SELECT repeat('a', 1024 * 1024 * 1024 - 5);
ERROR:  invalid memory alloc request size 1073741871
```

Here is the backtrace:

#0  palloc0 (size=1073741871) at /mnt/workspace/postgresql/build/../src/backend/utils/mmgr/mcxt.c:1103
#1  0x0000561925199faf in heap_form_tuple (tupleDescriptor=0x561927cb4310, values=0x561927cb4470,
isnull=0x561927cb4478)
    at /mnt/workspace/postgresql/build/../src/backend/access/common/heaptuple.c:1069
#2  0x00005619254879aa in tts_virtual_copy_heap_tuple (slot=0x561927cb4428) at
/mnt/workspace/postgresql/build/../src/backend/executor/execTuples.c:280
#3  0x000056192548a40c in ExecFetchSlotHeapTuple (slot=0x561927cb4428, materialize=true, shouldFree=0x7ffc9cc5197f)
    at /mnt/workspace/postgresql/build/../src/backend/executor/execTuples.c:1660
#4  0x000056192520e0c9 in heapam_tuple_insert (relation=0x7fdacaa9d3e0, slot=0x561927cb4428, cid=5, options=2,
bistate=0x561927cb83f0)
    at /mnt/workspace/postgresql/build/../src/backend/access/heap/heapam_handler.c:245
#5  0x00005619253b2edf in table_tuple_insert (rel=0x7fdacaa9d3e0, slot=0x561927cb4428, cid=5, options=2,
bistate=0x561927cb83f0)
    at /mnt/workspace/postgresql/build/../src/include/access/tableam.h:1376
#6  0x00005619253b3d73 in intorel_receive (slot=0x561927cb4428, self=0x561927c82430) at
/mnt/workspace/postgresql/build/../src/backend/commands/createas.c:586
#7  0x0000561925478d86 in ExecutePlan (estate=0x561927cb3ed0, planstate=0x561927cb4108, use_parallel_mode=false,
operation=CMD_SELECT,sendTuples=true, numberTuples=0,
 
    direction=ForwardScanDirection, dest=0x561927c82430, execute_once=true) at
/mnt/workspace/postgresql/build/../src/backend/executor/execMain.c:1667
#8  0x0000561925476735 in standard_ExecutorRun (queryDesc=0x561927cab990, direction=ForwardScanDirection, count=0,
execute_once=true)
    at /mnt/workspace/postgresql/build/../src/backend/executor/execMain.c:363
#9  0x000056192547654b in ExecutorRun (queryDesc=0x561927cab990, direction=ForwardScanDirection, count=0,
execute_once=true)
    at /mnt/workspace/postgresql/build/../src/backend/executor/execMain.c:307
#10 0x00005619253b3711 in ExecCreateTableAs (pstate=0x561927c35b00, stmt=0x561927b5aa70, params=0x0, queryEnv=0x0,
qc=0x7ffc9cc52370)

The heap_form_tupe() function need extra 48 bytes for HeapTupleHeaderData and HEAPTUPLESIZE.

If we use the following, everything is ok.

postgres=# CREATE TABLE myrepeat AS SELECT repeat('a', 1024 * 1024 * 1024 - 5 - 48);
SELECT 1


If we want to send the result to the client.  Here is an error.

postgres=# SELECT repeat('a', 1024 * 1024 * 1024 - 5);
ERROR:  out of memory
DETAIL:  Cannot enlarge string buffer containing 6 bytes by 1073741819 more bytes.

This is because the printtup() needs send to the number of attributes (2-byte)
and the length of data (4-byte) and then the data (1073741819-byte).

Do those mean we cannot store 1GB to a field [1] and send 1GB of data to the client?

[1] https://www.postgresql.org/docs/current/limits.html

-- 
Regrads,
Japin Li.
ChengDu WenWu Information Technology Co.,Ltd.



pgsql-hackers by date:

Previous
From: Michael Paquier
Date:
Subject: Re: Bump MIN_WINNT to 0x0600 (Vista) as minimal runtime in 16~
Next
From: Tom Lane
Date:
Subject: Re: Invalid memory alloc request size for repeat()