pg_restore: stack-buffer-overflow(read) in _tarGetHeader() in pg_backup_tar.c - Mailing list pgsql-bugs

From Ilia Kashintsev
Subject pg_restore: stack-buffer-overflow(read) in _tarGetHeader() in pg_backup_tar.c
Date
Msg-id CAF6ebR508tkZ1729YQVZJgMN-8pBDGWp7Mw=4UKJ6-Hx3ngyTw@mail.gmail.com
Whole thread
List 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



pgsql-bugs by date:

Previous
From: Michael Paquier
Date:
Subject: Re: BUG #19623: Postmaster livelocks respawning io workers when children die after crash restart; pg_ctl stop fails
Next
From: Tender Wang
Date:
Subject: Re: BUG #19633: Unexpected results of IN (subquery) with a non-deterministic collation