Avoid nullptr subtraction when flushing scalars at EOF #43

Closed
weaselbot wants to merge 2 commits from weaselbot/weaseljson:weaselbot/issue-40 into main
Member

Closes #40

Avoid undefined pointer subtraction when flushNumber/flushString are called at EOF with dataBegin/buf/writeBuf set to nullptr. Compute the scalar length by casting the pointers to intptr_t before subtracting, which is well-defined and matches the desired byte offset (zero when both pointers are null). Keep the empty-string fallback for the callback data pointer when it would otherwise be null.

Closes #40 Avoid undefined pointer subtraction when `flushNumber`/`flushString` are called at EOF with `dataBegin`/`buf`/`writeBuf` set to `nullptr`. Compute the scalar length by casting the pointers to `intptr_t` before subtracting, which is well-defined and matches the desired byte offset (zero when both pointers are null). Keep the empty-string fallback for the callback data pointer when it would otherwise be null.
weaselbot added 1 commit 2026-06-29 17:58:05 +00:00
Avoid nullptr subtraction when flushing scalars at EOF
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 52s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 50s
CI / pre-commit (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m30s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m24s
bd53e57b8e
When a scalar ends exactly at a chunk boundary, Parser3::parse resets
dataBegin and writeBuf to the new buf at the start of every call. On the
EOF call buf is null, so both pointers become null. The final
flushNumber/flushString then computed len as buf - dataBegin, i.e.
nullptr - nullptr, which is undefined behaviour in C++.

Compute the flush length safely: if dataBegin is null (or, for raw mode,
buf is null), treat the length as zero. Use an empty string literal as a
non-null data pointer for the zero-length, done=true callback so callers
still receive the completion signal.

Add a regression test covering a number that fills its chunk exactly and
is finalized by an EOF call.

Fixes #40
andrew requested changes 2026-06-29 18:36:49 +00:00
andrew left a comment
Owner

This is giving us slightly worse codegen. Does the UB go away if we cast to uintptr_t or intptr_t before subtracting?

Actually apparently this isn't even UB[0]. Just add a comment so we don't file this issue again.

[0]:

5.7  Additive operators                                     [expr.add]

...

8 If  the  value  0  is added to or subtracted from a pointer value, the
  result compares equal to the original pointer value.  If two  pointers
  point to the same object or function or both point one past the end of
  the same array or both are null, and the two pointers are  subtracted,
  the  result  compares  equal  to  the  value  0  converted to the type
  ptrdiff_t.
~~This is giving us slightly worse codegen. Does the UB go away if we cast to uintptr_t or intptr_t before subtracting?~~ Actually apparently this isn't even UB[0]. Just add a comment so we don't file this issue again. [0]: ``` 5.7 Additive operators [expr.add] ... 8 If the value 0 is added to or subtracted from a pointer value, the result compares equal to the original pointer value. If two pointers point to the same object or function or both point one past the end of the same array or both are null, and the two pointers are subtracted, the result compares equal to the value 0 converted to the type ptrdiff_t. ```
weaselbot added 1 commit 2026-06-29 19:14:41 +00:00
Avoid nullptr subtraction using intptr_t casts
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-arm64, ubuntu-latest-arm64, true) (pull_request) Successful in 51s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-arm64, ubuntu-latest-arm64, false) (pull_request) Successful in 53s
CI / pre-commit (pull_request) Successful in 52s
CI / build (-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++, clang-amd64, ubuntu-latest-amd64, true) (pull_request) Successful in 1m30s
CI / build (-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, gcc-amd64, ubuntu-latest-amd64, false) (pull_request) Successful in 1m27s
bf3f2fe810
Andrew's review on the previous fix noted that the nullptr checks produced slightly worse codegen. Replace the pointer subtraction with intptr_t subtraction, which avoids the undefined behaviour of subtracting two null pointers without introducing extra branches.
andrew closed this pull request 2026-06-29 19:56:10 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#43