pg_restore: stack-buffer-overflow(read) in _tarGetHeader() in pg_backup_tar.c

From: Ilia Kashintsev <ilia(dot)kashintsev(at)gmail(dot)com>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Subject: pg_restore: stack-buffer-overflow(read) in _tarGetHeader() in pg_backup_tar.c
Date: 2026-08-20 13:22:52
Message-ID: CAF6ebR508tkZ1729YQVZJgMN-8pBDGWp7Mw=4UKJ6-Hx3ngyTw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

Hello maintainers!

security@ checked this report and found it to be not a vuln,
since "The OOB read only affects the return value of strlcpy(),
and this caller ignores that return value".

There is a stack-buffer-overflow on unknown address in _tarGetHeader().

It occurs because strlcpy() is being called with src as an argument,
which, according to the comment on line 1167, might not be null-terminated.
In order to calculate its return value, strlcpy is going to walk
through src until '\0', which is missing, resulting in OOB read.

pg_backup_tar.c:1167-1168:

/* Name field is 100 bytes, might not be null-terminated */
strlcpy(tag, &h[TAR_OFFSET_NAME], 100 + 1); <------

Information from the bug reporting form:
PostgreSQL version: 19beta2
Operating system: Ubuntu 24.04.4 LTS

Steps to reproduce:

1)
Build the project with ASAN,
ensure sanitized src/port/strlcpy.c is being used;

sudo mkdir -p /builds2
sudo chown "$(whoami)" /builds2

mkdir -p asan_build
cd asan_build
export CC=clang
export CXX=clang++
export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
export CXXFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
export LDFLAGS="-fsanitize=address"

../postgres/configure --prefix=/builds2/pg-asan ac_cv_func_strlcpy=no
ac_cv_have_decl_strlcpy=no
make -j
sudo make install

2) Run the example:

echo 'dG8wNzJhdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMDA2NDQAMDAwMDAw
MAAwMDAwMDAwADAwMDAwMDAwMDczADAwMDAwMDAwMDAwADAwNzIyNAAwMAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1c3RhcgAwMAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw'
| base64 -d > inp.bin

/builds2/pg-asan/bin/pg_restore -f dump.sql inp.bin

Sanitizer output:
==264230==ERROR: AddressSanitizer: stack-buffer-overflow on address
0x764aa7100a20 at pc 0x56bb55abde12 bp 0x7ffe83d4a890 sp
0x7ffe83d4a888
READ of size 1 at 0x764aa7100a20 thread T0
#0 0x56bb55abde11 in strlcpy
/home/reproduce/asan_build/../postgres/src/port/strlcpy.c:66:10
#1 0x56bb55aac83b in _tarGetHeader
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_tar.c:1168:2
#2 0x56bb55aab7ac in _tarPositionTo
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_tar.c:1102:8
#3 0x56bb55aab7ac in tarOpen
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_tar.c:309:8
#4 0x56bb55aa93de in InitArchiveFmt_Tar
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_tar.c:222:13
#5 0x56bb55a8b4ff in _allocAH
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_archiver.c:2482:4
#6 0x56bb55a8bb0e in OpenArchive
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_archiver.c:254:7
#7 0x56bb55a7c631 in main
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_restore.c:488:7
#8 0x764aa8cd41c9 in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#9 0x764aa8cd428a in __libc_start_main csu/../csu/libc-start.c:360:3
#10 0x56bb559a1974 in _start
(/builds2/pg-asan/bin/pg_restore+0x43974) (BuildId:
b54d44745ecb5f358a7ac913ad08b66dda84c596)

Address 0x764aa7100a20 is located in stack of thread T0 at offset 544 in frame
#0 0x56bb55aac5ff in _tarGetHeader
/home/reproduce/asan_build/../postgres/src/bin/pg_dump/pg_backup_tar.c:1116

This frame has 2 object(s):
[32, 544) 'h' (line 1118) <== Memory access at offset 544
overflows this variable
[608, 709) 'tag' (line 1119)
HINT: this may be a false positive if your program uses some custom
stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow
/home/reproduce/asan_build/../postgres/src/port/strlcpy.c:66:10 in
strlcpy
Shadow bytes around the buggy address:
0x764aa7100780: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x764aa7100800: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x764aa7100a00: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 00 00 00 00
0x764aa7100a80: 00 00 00 00 00 00 00 00 05 f3 f3 f3 f3 f3 f3 f3
0x764aa7100b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x764aa7100c80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==264230==ABORTING

Suggested fix:
Not sure this is entirely correct, but bounded copy and explicit '\0'
resolves the issue.

diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 542fdb4..df3b3af 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -1165,7 +1165,8 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
}

/* Name field is 100 bytes, might not be null-terminated */
- strlcpy(tag, &h[TAR_OFFSET_NAME], 100 + 1);
+ memcpy(tag, &h[TAR_OFFSET_NAME], 100);
+ tag[100] = '\0';

len = read_tar_number(&h[TAR_OFFSET_SIZE], 12);

Best regards,
Ilia Kashintsev

Browse pgsql-bugs by date

  From Date Subject
Previous Message Heikki Linnakangas 2026-08-20 13:19:23 Re: SIGSEGV in dynahash