Thread

Commits

  1. intarray: Prevent out-of-bound memory reads with gist__int_ops

  2. Remove usage of ArrayType->flags field, use pgsql's macros BITS_PER_BYTE instead

  1. BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    The Post Office <noreply@postgresql.org> — 2023-04-06T09:00:01Z

    The following bug has been logged on the website:
    
    Bug reference:      17888
    Logged by:          Alexander Lakhin
    Email address:      exclusion@gmail.com
    PostgreSQL version: 15.2
    Operating system:   Ubuntu 22.04
    Description:        
    
    The following script:
    CREATE EXTENSION intarray;
    CREATE TABLE test__int(a int[]);
    CREATE INDEX test_idx on test__int using gist (a gist__int_ops);
    INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 1001) x)
    FROM generate_series(1, 10);
    
    causes a server crash:
    CREATE INDEX
    NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    gist__intbig_ops opclass instead
    NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    gist__intbig_ops opclass instead
    NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    gist__intbig_ops opclass instead
    server closed the connection unexpectedly
            This probably means the server terminated abnormally
            before or while processing the request.
    
    with the stack trace:
    Core was generated by `postgres: law regression [local] INSERT              
                            '.
    Program terminated with signal SIGSEGV, Segmentation fault.
    
    warning: Section `.reg-xstate/2702527' in core file too small.
    #0  0x00005610b8d00aa3 in GetMemoryChunkContext (pointer=0x7ff21ceac7b0)
        at ../../../../src/include/utils/memutils.h:131
    131             AssertArg(MemoryContextIsValid(context));
    (gdb) bt
    #0  0x00005610b8d00aa3 in GetMemoryChunkContext (pointer=0x7ff21ceac7b0)
        at ../../../../src/include/utils/memutils.h:131
    #1  0x00005610b8d02ea7 in pfree (pointer=0x7ff21ceac7b0) at mcxt.c:1177
    #2  0x00007ff263238bb4 in g_int_decompress (fcinfo=0x7ffe00cda520) at
    _int_gist.c:345
    #3  0x00005610b8cc93f2 in FunctionCall1Coll (flinfo=0x5610ba16cf08,
    collation=100, arg1=94629841722496) at fmgr.c:1124
    #4  0x00005610b85c499e in gistdentryinit (giststate=0x5610ba16bce0, nkey=0,
    e=0x5610ba19dc80, k=140677843961776, 
        r=0x7ff21a1a80b8, pg=0x7ff21ceab780 "", o=1, l=false, isNull=false) at
    gistutil.c:562
    #5  0x00005610b85c1fd1 in gistSplitByKey (r=0x7ff21a1a80b8,
    page=0x7ff21ceab780 "", itup=0x5610ba19dc20, len=3, 
        giststate=0x5610ba16bce0, v=0x7ffe00cda750, attno=0) at
    gistsplit.c:644
    #6  0x00005610b85b39ca in gistSplit (r=0x7ff21a1a80b8, page=0x7ff21ceab780
    "", itup=0x5610ba19dc20, len=3, 
        giststate=0x5610ba16bce0) at gist.c:1450
    #7  0x00005610b85af946 in gistplacetopage (rel=0x7ff21a1a80b8, freespace=0,
    giststate=0x5610ba16bce0, buffer=1804, 
        itup=0x7ffe00cdac00, ntup=1, oldoffnum=0, newblkno=0x0, leftchildbuf=0,
    splitinfo=0x7ffe00cdabb0, 
        markfollowright=true, heapRel=0x7ff21a1acef0, is_build=false) at
    gist.c:309
    #8  0x00005610b85b34c5 in gistinserttuples (state=0x7ffe00cdacc0,
    stack=0x7ffe00cdac90, giststate=0x5610ba16bce0, 
        tuples=0x7ffe00cdac00, ntup=1, oldoffnum=0, leftchild=0, rightchild=0,
    unlockbuf=false, unlockleftchild=false)
        at gist.c:1278
    #9  0x00005610b85b33f8 in gistinserttuple (state=0x7ffe00cdacc0,
    stack=0x7ffe00cdac90, giststate=0x5610ba16bce0, 
        tuple=0x5610ba19cbe0, oldoffnum=0) at gist.c:1231
    #10 0x00005610b85b1f1e in gistdoinsert (r=0x7ff21a1a80b8,
    itup=0x5610ba19cbe0, freespace=0, giststate=0x5610ba16bce0, 
        heapRel=0x7ff21a1acef0, is_build=false) at gist.c:886
    #11 0x00005610b85af35c in gistinsert (r=0x7ff21a1a80b8,
    values=0x7ffe00cdae80, isnull=0x7ffe00cdae60, 
        ht_ctid=0x5610ba15e510, heapRel=0x7ff21a1acef0,
    checkUnique=UNIQUE_CHECK_NO, indexUnchanged=false, 
        indexInfo=0x5610ba15f9c8) at gist.c:183
    #12 0x00005610b8615242 in index_insert (indexRelation=0x7ff21a1a80b8,
    values=0x7ffe00cdae80, isnull=0x7ffe00cdae60, 
        heap_t_ctid=0x5610ba15e510, heapRelation=0x7ff21a1acef0,
    checkUnique=UNIQUE_CHECK_NO, indexUnchanged=false, 
        indexInfo=0x5610ba15f9c8) at indexam.c:193
    #13 0x00005610b8878e87 in ExecInsertIndexTuples
    (resultRelInfo=0x5610ba1446e8, slot=0x5610ba15e4e0, 
        estate=0x5610ba142f90, update=false, noDupErr=false, specConflict=0x0,
    arbiterIndexes=0x0) at execIndexing.c:416
    #14 0x00005610b88c6a99 in ExecInsert (context=0x7ffe00cdb130,
    resultRelInfo=0x5610ba1446e8, slot=0x5610ba15e4e0, 
        canSetTag=true, inserted_tuple=0x0, insert_destrel=0x0) at
    nodeModifyTable.c:1120
    #15 0x00005610b88cb096 in ExecModifyTable (pstate=0x5610ba1444d0) at
    nodeModifyTable.c:3764
    #16 0x00005610b88879a4 in ExecProcNodeFirst (node=0x5610ba1444d0) at
    execProcnode.c:464
    #17 0x00005610b887abd1 in ExecProcNode (node=0x5610ba1444d0) at
    ../../../src/include/executor/executor.h:259
    #18 0x00005610b887dae4 in ExecutePlan (estate=0x5610ba142f90,
    planstate=0x5610ba1444d0, use_parallel_mode=false, 
        operation=CMD_INSERT, sendTuples=false, numberTuples=0,
    direction=ForwardScanDirection, dest=0x5610ba13cb08, 
        execute_once=true) at execMain.c:1636
    #19 0x00005610b887b308 in standard_ExecutorRun (queryDesc=0x5610ba07a360,
    direction=ForwardScanDirection, count=0, 
        execute_once=true) at execMain.c:363
    #20 0x00005610b887b0f3 in ExecutorRun (queryDesc=0x5610ba07a360,
    direction=ForwardScanDirection, count=0, 
        execute_once=true) at execMain.c:307
    #21 0x00005610b8af4194 in ProcessQuery (plan=0x5610ba13ca18, 
        sourceText=0x5610ba057460 "INSERT INTO test__int SELECT array(SELECT x
    FROM generate_series(1, 1001) x) FROM generate_series(1, 10);", params=0x0,
    queryEnv=0x0, dest=0x5610ba13cb08, qc=0x7ffe00cdb580) at pquery.c:160
    #22 0x00005610b8af5d8a in PortalRunMulti (portal=0x5610ba0e7070,
    isTopLevel=true, setHoldSnapshot=false, 
        dest=0x5610ba13cb08, altdest=0x5610ba13cb08, qc=0x7ffe00cdb580) at
    pquery.c:1277
    #23 0x00005610b8af526d in PortalRun (portal=0x5610ba0e7070,
    count=9223372036854775807, isTopLevel=true, run_once=true, 
        dest=0x5610ba13cb08, altdest=0x5610ba13cb08, qc=0x7ffe00cdb580) at
    pquery.c:791
    #24 0x00005610b8aee050 in exec_simple_query (
        query_string=0x5610ba057460 "INSERT INTO test__int SELECT array(SELECT x
    FROM generate_series(1, 1001) x) FROM generate_series(1, 10);") at
    postgres.c:1250
    #25 0x00005610b8af2f74 in PostgresMain (dbname=0x5610ba085c48 "regression",
    username=0x5610ba085c28 "law")
        at postgres.c:4593
    #26 0x00005610b8a17e7c in BackendRun (port=0x5610ba07d360) at
    postmaster.c:4511
    #27 0x00005610b8a17703 in BackendStartup (port=0x5610ba07d360) at
    postmaster.c:4239
    #28 0x00005610b8a13678 in ServerLoop () at postmaster.c:1806
    #29 0x00005610b8a12dd8 in PostmasterMain (argc=3, argv=0x5610ba052670) at
    postmaster.c:1478
    #30 0x00005610b89065f4 in main (argc=3, argv=0x5610ba052670) at main.c:202
    
    IIUC, this check in g_int_decompress():
    	lenin = ARRNELEMS(in);
    
    	if (lenin < 2 * num_ranges)
    	{							/* not compressed value */
    ...
    assumes that a larger number of elements is a sign of compressed value,
    but
    g_int_compress() can produce a non-compressed value with such number of
    elements (emitting a notice just for information).
    
    I'd also note that a variation with 2001 elements and the different order:
    INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 2001) x)
    FROM generate_series(1, 10);
    CREATE INDEX test_idx on test__int using gist (a gist__int_ops);
    
    gives a confusing message:
    ERROR:  index row size 8040 exceeds maximum 8152 for index "test_idx"
    
    Reproduced on REL_11_STABLE .. master.
    
    
  2. Re: BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    Richard Guo <guofenglinux@gmail.com> — 2023-04-07T08:57:27Z

    On Fri, Apr 7, 2023 at 4:21 AM PG Bug reporting form <noreply@postgresql.org>
    wrote:
    
    > The following script:
    > CREATE EXTENSION intarray;
    > CREATE TABLE test__int(a int[]);
    > CREATE INDEX test_idx on test__int using gist (a gist__int_ops);
    > INSERT INTO test__int SELECT array(SELECT x FROM generate_series(1, 1001)
    > x)
    > FROM generate_series(1, 10);
    >
    > causes a server crash:
    > CREATE INDEX
    > NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    > gist__intbig_ops opclass instead
    > NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    > gist__intbig_ops opclass instead
    > NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    > gist__intbig_ops opclass instead
    
    
    In g_int_compress,
    
     if (ARRNELEMS(r) >= 2 * num_ranges)
         elog(NOTICE, "input array is too big (%d maximum allowed, %d current),
    use gist__intbig_ops opclass instead",
              2 * num_ranges - 1, ARRNELEMS(r));
    
    Wondering why elog with NOTICE rather than Error here.
    
    Thanks
    Richard
    
  3. Re: BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    Alexander Law <exclusion@gmail.com> — 2023-05-31T06:00:00Z

    07.04.2023 11:57, Richard Guo wrote:
    >
    > On Fri, Apr 7, 2023 at 4:21 AM PG Bug reporting form <noreply@postgresql.org> wrote:
    >
    >     NOTICE:  input array is too big (199 maximum allowed, 1001 current), use
    >     gist__intbig_ops opclass instead
    >
    >
    > In g_int_compress,
    >
    >  if (ARRNELEMS(r) >= 2 * num_ranges)
    >      elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
    >           2 * num_ranges - 1, ARRNELEMS(r));
    >
    > Wondering why elog with NOTICE rather than Error here.
    
    The NOTICE appeared there in commit 08ee64ebf5 (from year 2005) in an
    attempt to get rid of flags (ISLEAFKEY(in)) [1] and probably expressed
    an intention to play gently (don't break compatibility?).
    
    The patch proposed at [2] looks correct to me. Thank you, Ankit!
    Though I would add a simple case to the intarray regression test and also
    add errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED) to other two places in
    _int_gist.c for the sake of consistency.
    
    Please look at the patches attached.
    
    [1] https://www.postgresql.org/message-id/43786274.3020200%40sigaev.ru
    [2] https://www.postgresql.org/message-id/796b65c3-57b7-bddf-b0d5-a8afafb8b627%40gmail.com
  4. Re: BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    Michael Paquier <michael@paquier.xyz> — 2023-06-14T04:21:40Z

    On Wed, May 31, 2023 at 09:00:00AM +0300, Alexander Lakhin wrote:
    > The NOTICE appeared there in commit 08ee64ebf5 (from year 2005) in an
    > attempt to get rid of flags (ISLEAFKEY(in)) [1] and probably expressed
    > an intention to play gently (don't break compatibility?).
    
    Good question.  This commit or its related thread don't tell much.
    FWIW, these are two things that don't make much sense to me:
    1) to allow the compression to work with that many elements with its
    code unable to handle that, while storing incorrect data.
    2) the decompression fails entirely to cope with that.
    
    > The patch proposed at [2] looks correct to me. Thank you, Ankit!
    > Though I would add a simple case to the intarray regression test and also
    > add errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED) to other two places in
    > _int_gist.c for the sake of consistency.
    
    The error is different in ~9.6 and 10~, where we would not get a crash
    before that.  But We do get a lot of write past chunk end WARNINGs
    instead.
    
    Saying that, being able to corrupt the server at will with a gist
    operator that can be installed is no good either, so your suggestion
    of made of the two patches is OK by me.  Based on my read of the code,
    this prevents the compression to store data it would not be able to
    handle on decompression.  Now, I have to admit that I am not the most
    verse with this area.
    
    Likely that's something we'd better backpatch, to avoid people playing
    with that too much in the back-branches.  Thoughts or objections?
    --
    Michael
    
  5. Re: BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    Michael Paquier <michael@paquier.xyz> — 2023-06-15T04:53:36Z

    On Wed, Jun 14, 2023 at 01:21:40PM +0900, Michael Paquier wrote:
    > Likely that's something we'd better backpatch, to avoid people playing
    > with that too much in the back-branches.  Thoughts or objections?
    
    Well, we could perhaps revisit the choice of 08ee64e to remove the
    LEAF flag from the data stored.  However, seeing the lack of
    complaints for the past 18 years with users storing large arrays under
    gist__int_ops, spending more time on this is not really appealing,
    either.  Or we would have heard about that because the decompression
    immediately breaks for this case.
    
    I have spent some time on that today, and applied what has been
    suggested to restrict the usage of this operator when inserting data 
    for a leaf page, so as the decompression does not get crazy when
    retrieving the contents of the leaf pages.
    --
    Michael
    
  6. Re: BUG #17888: Incorrect memory access in gist__int_ops for an input array with many elements

    Alexander Law <exclusion@gmail.com> — 2023-06-15T10:00:00Z

    Hi Michael,
    
    15.06.2023 07:53, Michael Paquier wrote:
    > I have spent some time on that today, and applied what has been
    > suggested to restrict the usage of this operator when inserting data
    > for a leaf page, so as the decompression does not get crazy when
    > retrieving the contents of the leaf pages.
    
    Thank you for paying attention to it!
    I closed https://commitfest.postgresql.org/43/4280/ as committed.
    
    The confusing message I noted previously:
    ERROR:  index row size 8040 exceeds maximum 8152 for index "test_idx"
    is not emitted now, although I think it still can be produced for some
    other gist indexes. But that's a matter for another day, I suppose.
    
    Best regards,
    Alexander