pico_printf exhibits undefined behavior with null %s argument, even with 0 precision #3167
Labels
No labels
bazel
bluetooth
board settings
boot_stage2
bug
build
c++
can't reproduce
clang
clib-interface
cmsis
code cleanliness
code size
compiler version issues
cyw43_driver
debug
discussion
documentation
duplicate
dynamic_clock
elf2uf2
enhancement
errata
filesystem
flash layout
floating point
FreeRTOS
hardware_adc
hardware_claim
hardware_clocks
hardware_dma
hardware_exception
hardware_flash
hardware_gpio
hardware_i2c
hardware_irq
hardware_pio
hardware_pwm
hardware_rcp
hardware_rtc
hardware_spi
hardware_sync
hardware_uart
hardware_vreg
hardware_watchdog
hardware_xosc
help wanted
host
IAR
installation
invalid
linker scripts
lto
lwip
mbedtls
missing API
needs reporter detail
networking
newlib
newlib-multicore
non-c
non-cmake
non-gcc
openocd
orphan PR
OS integration
pico_async_context
pico_base
pico_binary_info
pico_flash
picolibc
pico_lwip
pico_malloc
pico_multicore
pico_printf
pico_runtime
pico_stdio
pico_stdio_usb
pico_sync
pico_time
pico_util
pioasm
port
question
review
review2
rtos interop
runtime init
static library
svd
test
tinyusb
vscode
wifi integration
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-raspberrypi/pico-sdk#3167
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I wanted to get a team member's thoughts on this before just submitting a PR.
Summary:
printf("%.*s", 0, (char*)0)dereferences the null pointer despite the0precision sub-specifier. I propose a small change to_vsnprintfto instead define the behavior to print nothing, consistent with other implementations.EDIT: technically, functions like
sprintfwould still print the nul terminator (compared to, say,snprintfwith size 0), I forgot because I've actually been usingvfctprintf, which doesn't output nuls.I ran into this through a combination of trying to use 'pointer-and-length' strings instead of nul-terminated ones in my recent project, and compiling with UBSan. Without UBSan, the behavior is (so far) actually as desired, so I would understand if this is considered very low priority. But it would be nice to do less patching of my (and my teammate's) copies of the SDK in the future.
Specifically, I've been using
typedef struct { char* data; size_t len; } str;in my code and it works nicely with things like parsing. One nicety is that zero-initializing it is a valid empty string, for the purposes of my other string processing functions. But after I started using UBSan, I found out thatprintf("%.*s", s.len, s.data)derefs the pointer, even with the zero length. To be fair, this seems to be allowed by the standard, but every libc I've looked at (including newlib and picolibc, as well as glibc and musl) define the behavior. So it would be nice to be consistent.I saw https://github.com/raspberrypi/pico-sdk/issues/2694, so one option would be to punt and wait for that update. Unfortunately for me, I would/will have to try to submit a PR to eyalroz as their fork currently would print
"(null)"unconditionally, ignoring the precision.The simplest fix (which I use locally) is to add
if (!p) p = "";in between the lines:github.com/raspberrypi/pico-sdk@98a542c1a6/src/rp2_common/pico_printf/printf.c (L810-L811)Of course it could instead be
p = "(null)"for debugability in cases someone passes a null pointer accidentally to%s(and aren't using UBSan), plus full consistency with other implementations. This would still print the desired 0 characters with a precision of 0. It just takes 6 more bytes.Related UB note: I can submit a separate issue/PR for this if appropriate, but I thought I'd quickly seek comments about it here: another patch I had to make is adding
__attribute__((no_sanitize("undefined")))to runtime_init as the(*p)();on line 206 also trips UBSan. I didn't know what to do about it that deep in pre-main code so I ignored it with the attribute. This and the_vsnprintfare the only instances I've found in the sdk proper so far. (however tinyusb 0.18 derefs null all the time!) Granted there are many parts of the sdk I've yet to use, but I figure if I was a maintainer, I'd want to know about instances of UB.Thanks in advance, please let me know what you think.