Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -3182,10 +3182,17 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
size_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
}
mrb_int capa = str_modify_cat(mrb, s, (mrb_int)len);
/* The overlap has to be recognized against the buffer `ptr` was taken
from, which is the one `s` holds now. `str_modify_cat()` either appends
inside the shared allocation, where `ptr` stays valid and the offset is
the same answer reached another way, or detaches `s` onto a fresh buffer
and releases the old one, where `ptr` is neither inside the new buffer
nor safe to read. Recording the offset ahead of the call covers both
without having to know which path ran. */
if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) {
off = ptr - RSTR_PTR(s);
}
Comment on lines 3192 to 3194

@coderabbitai coderabbitai Bot Aug 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/sh
set -eu

rg -n -C 4 'uintptr_t|RSTR_CAPA|RSTR_PTR\(s\)|ptrdiff_t off' \
  src/string.c include/mruby

Repository: mruby/mruby

Length of output: 20879


🏁 Script executed:

#!/bin/bash
set -eu

printf '--- files ---\n'
git ls-files | rg '(^|/)(README|.*\.md|.*\.txt|CMakeLists\.txt|Makefile|\.clang.*|.*\.inc|.*\.h)$' | head -100 || true

printf '\n--- mrb_str_cat and call sites ---\n'
rg -n -C 3 '\bmrb_str_cat\(|mrb_str_cat_' -- .

printf '\n--- pointer relational/subtraction patterns around RSTR_PTR ---\n'
rg -n -C 2 '\b(ptr|p|end|boundary|mrb_str_ptr).*[><]=|[><=].*(ptr|p|end|boundary|mrb_str_ptr)|ptr - RSTR_PTR\(|RSTR_PTR\(s\) -|RSTRING_PTR\(str0\)' src/include include src string.c -g '*.[ch]' | head -250

Repository: mruby/mruby

Length of output: 240


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- files ---'
git ls-files | rg '(^|/)(README|.*\.md|.*\.txt|CMakeLists\.txt|Makefile|\.clang.*|.*\.inc|.*\.h)$' | head -100 || true

printf '%s\n'
printf '%s\n' '--- mrb_str_cat and call sites ---'
rg -n -C 3 '\bmrb_str_cat\(|mrb_str_cat_' -- .

printf '%s\n'
printf '%s\n' '--- pointer relational/subtraction patterns around RSTR_PTR ---'
rg -n -C 2 '\b(ptr|p|end|boundary|mrb_str_ptr).*[><]=|[><=].*(ptr|p|end|boundary|mrb_str_ptr)|ptr - RSTR_PTR\(|RSTR_PTR\(s\) -|RSTRING_PTR\(str0\)' src include -g '*.[ch]' | head -250 || true

Repository: mruby/mruby

Length of output: 50367


Use integer address math before modifying str.

mrb_str_cat() accepts pointers from any object. In src/string.c:3144-3145, comparing/subtracting ptr against RSTR_PTR(s) before mrb_str_modify() can compare unrelated pointers. Use uintptr_t arithmetic for the containment check before mrb_str_modify() moves the string buffer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/string.c` around lines 3144 - 3146, Update the pointer containment and
offset calculation in mrb_str_cat to convert ptr and RSTR_PTR(s) to uintptr_t
before comparing or subtracting, avoiding unrelated-pointer operations while
preserving the existing length bounds and computed off value before
mrb_str_modify().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison this points at is not introduced here. Master carries the same expression, and this PR moves it above str_modify_cat() without changing its form, so the two pointers are as related or unrelated as they were before. The position is what this PR is about.

The formal point stands on its own merits: relational comparison and subtraction between pointers into different objects is undefined by C11 6.5.8p5 and 6.5.6p9, and a containment check has no portable spelling other than uintptr_t. mruby already spells one that way in is_pool_memory() in mrbgems/mruby-bigint/core/bigint.c. If that is worth applying to mrb_str_cat(), it should come as its own change against master rather than ride along with an ordering fix.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@takumin, agreed. This finding is outside the scope of #7043.

The PR preserves the existing pointer expression and changes only when mrb_str_cat() records overlap. I will not treat the uintptr_t portability concern as a required change for this PR.

If you want, I can open a separate issue for a master-targeted update to the containment check in mrb_str_cat().

(ᵔᴥᵔ)

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issue needed. That change is already up as #7051 against master, which converts both operands of the containment check to uintptr_t. It also records that whichever of the two lands first, the other gets rebased onto it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@takumin, understood. #7051 already contains the uintptr_t containment-check fix and the rebasing behavior for either allocation order.

No follow-up issue is needed. #7043 remains scoped to recording the overlap offset before str_modify_cat().

You are interacting with an AI system.

mrb_int capa = str_modify_cat(mrb, s, (mrb_int)len);

if (capa <= total) {
if (capa == 0) capa = 1;
Expand Down
Loading