> On Sep 21, 2026, at 13:04, Kirill Reshke <[email protected]> wrote:
> 
> On Mon, 21 Sept 2026 at 08:31, Chao Li <[email protected]> wrote:
>> 
>> Hi,
>> 
>> I happened to encounter a psql crash that could not be reproduced reliably 
>> with a normal build. However, AddressSanitizer reproduces it consistently.
>> 
>> 1. Build psql with AddressSanitizer
>> ```
>> % ./configure CFLAGS='-O1 -g -fsanitize=address -fno-omit-frame-pointer' 
>> LDFLAGS='-fsanitize=address’
>> % make -C src/bin/psql psql
>> ```
>> Note: On my MacBook, gcc points to clang.
>> 
>> 2. Run psql and set PROMPT1 to an unterminated variable
>> ```
>> evantest=# \set PROMPT1 '%:aa'
>> =================================================================
>> ==34121==ERROR: AddressSanitizer: heap-buffer-overflow on address 
>> 0x602000001f35 at pc 0x0001024be288 bp 0x00016d976930 sp 0x00016d976928
>> READ of size 1 at 0x602000001f35 thread T0
>>    #0 0x0001024be284 in get_prompt prompt.c:103
>>    #1 0x0001024bc1a8 in MainLoop mainloop.c:166
>>    #2 0x0001024ce05c in main startup.c:471
>>    #3 0x0001827ac4e0 in start+0x1b4c (dyld:arm64e+0x204e0)
>> 
>> 0x602000001f35 is located 0 bytes after 5-byte region 
>> [0x602000001f30,0x602000001f35)
>> allocated by thread T0 here:
>>    #0 0x000103172b54 in strdup+0x108 
>> (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3ab54)
>>    #1 0x0001025080e4 in pg_strdup fe_memutils.c:101
>>    #2 0x0001024dcf5c in SetVariable variables.c:316
>>    #3 0x0001024952e4 in exec_command_set command.c:2923
>>    #4 0x00010248b8b8 in exec_command command.c:445
>>    #5 0x0001024889e4 in HandleSlashCmds command.c:260
>>    #6 0x0001024bcb70 in MainLoop mainloop.c:499
>>    #7 0x0001024ce05c in main startup.c:471
>>    #8 0x0001827ac4e0 in start+0x1b4c (dyld:arm64e+0x204e0)
>> 
>> SUMMARY: AddressSanitizer: heap-buffer-overflow prompt.c:103 in get_prompt
>> Shadow bytes around the buggy address:
>>  0x602000001c80: fa fa 03 fa fa fa 02 fa fa fa 00 03 fa fa 06 fa
>>  0x602000001d00: fa fa 02 fa fa fa 00 02 fa fa 00 02 fa fa 00 05
>>  0x602000001d80: fa fa 00 02 fa fa 00 07 fa fa 02 fa fa fa 00 02
>>  0x602000001e00: fa fa 00 02 fa fa 00 02 fa fa 00 02 fa fa 00 02
>>  0x602000001e80: fa fa 00 07 fa fa 00 fa fa fa 00 04 fa fa fd fa
>> =>0x602000001f00: fa fa fd fa fa fa[05]fa fa fa fd fa fa fa fa fa
>>  0x602000001f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>>  0x602000002000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>>  0x602000002080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>>  0x602000002100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>>  0x602000002180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
>> 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
>> ==34121==ABORTING
>> zsh: abort      psql -d evantest
>> ```
>> 
>> The problem is that the current code assumes a terminating “:" exists. When 
>> it does, "p += nameend + 1" makes p point to the terminating colon, and the 
>> for loop's increment advances p to the string's terminating '\0'. When the 
>> terminating colon is absent, the same assignment already makes p point to 
>> '\0', and the for loop's increment advances p one past the end of the 
>> string. The next loop condition then dereferences p out of bounds. If that 
>> invalid read yields a nonzero value, the loop continues and can perform 
>> further out-of-bounds reads.
>> 
>> The fix is straightforward, only advance over the terminating colon when it 
>> exists. The same problem also exists for the %\command`` escape.
>> 
>> See the attached patch for details.
>> 
>> Best regards,
>> --
>> Chao Li (Evan)
>> HighGo Software Co., Ltd.
>> https://www.highgo.com/
>> 
> 
> 
> Hi!
> I think this is indeed a real issue, please register CF item for this.
> Looks like this code is dating back to a45195a191 [0], so this bug
> exists in all supported versions.

Thank you very much for the review. Yes, this is an old bug.

> 
> Code fix itself is fine but maybe write it like `if p[1] != NULL` for
> consistency.
> 

Make sense. We should use ‘\0’ instead of NULL.

> 
> [0] 
> https://github.com/postgres/postgres/blob/a45195a191eec367a4f305bb71ab541d17a3b9f9/src/bin/psql/prompt.c#L227
> 
> -- 
> Best regards,
> Kirill Reshke

PFA v2 - addressed Kirill’s comment.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/




Attachment: v2-0001-psql-Avoid-over-reading-unterminated-prompt-escap.patch
Description: Binary data

Reply via email to