]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix out-of-bounds access in autoprewarm worker
authorTomas Vondra <tomas.vondra@postgresql.org>
Fri, 26 Jun 2026 17:34:14 +0000 (19:34 +0200)
committerTomas Vondra <tomas.vondra@postgresql.org>
Fri, 26 Jun 2026 17:48:20 +0000 (19:48 +0200)
The read stream callback apw_read_stream_next_block() advances p->pos
through the block_info array. When processing the last block, it
increments p->pos to prewarm_stop_idx before returning. The callback
itself is safe because it checks bounds before accessing the array.

However, the caller assigned blk from block_info[i] at the end of the
loop body, before the loop condition was re-evaluated. When i equaled
prewarm_stop_idx, this accessed memory beyond the allocated DSM segment,
causing a segfault.

Restructure the loop to check bounds at the top and assign blk at the
beginning of the loop body, where it is always safe. This avoids the
need for an explicit bounds check at the end.

Backpatch to 18, where the bug was introduced by commit 6acab8bdbcda.

Author: Matheus Alcantara <mths.dev@pm.me>
Reported-by: Glauber Batista <glauberrbatista@gmail.com>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Tomas Vondra <tomas@vondra.me>
Backpatch-through: 18
Discussion: https://www.postgresql.org/message-id/CAO%2B_mTQgQyTYwDh%3DU8iTnsDmOGyWsZJjUV31SmEYwmw6_xY6Bw%40mail.gmail.com

contrib/pg_prewarm/autoprewarm.c

index c01b9c7e6a4d669b682ac04286c9168387152431..b5ac0e9ceb8e62d2bf4da07678318910e2d29d19 100644 (file)
@@ -573,16 +573,23 @@ autoprewarm_database_main(Datum main_arg)
                 * move on.
                 */
                while (i < apw_state->prewarm_stop_idx &&
-                          blk.tablespace == tablespace &&
-                          blk.filenumber == filenumber &&
                           have_free_buffer())
                {
-                       ForkNumber      forknum = blk.forknum;
+                       ForkNumber      forknum;
                        BlockNumber nblocks;
                        struct AutoPrewarmReadStreamData p;
                        ReadStream *stream;
                        Buffer          buf;
 
+                       blk = block_info[i];
+
+                       /* Stop when we reach a different relation. */
+                       if (blk.tablespace != tablespace ||
+                               blk.filenumber != filenumber)
+                               break;
+
+                       forknum = blk.forknum;
+
                        /*
                         * smgrexists is not safe for illegal forknum, hence check whether
                         * the passed forknum is valid before using it in smgrexists.
@@ -644,9 +651,12 @@ autoprewarm_database_main(Datum main_arg)
 
                        read_stream_end(stream);
 
-                       /* Advance i past all the blocks just prewarmed. */
+                       /*
+                        * Advance i past all the blocks just prewarmed. Note that the
+                        * callback might have advanced the index beyond the last valid
+                        * block, so don't access block_info[i] yet.
+                        */
                        i = p.pos;
-                       blk = block_info[i];
                }
 
                relation_close(rel, AccessShareLock);