Skip to content

Dynamic Rust strings cause use-after-free and memory corruption #75

@TheRedDeveloper

Description

@TheRedDeveloper

Passing dynamically generated strings (format!, String::from...) to text nodes can result in memory corruption, garbage text, or crashes, because the bindings only store a pointer to the string data, which may be invalid after the Rust stack frame ends.

Example:

fn draw_score(clay: &mut ClayLayoutScope, score: u32) {
    // This string is allocated on the stack and will be freed after this function
    let score_text = format!("Score: {}", score);
    clay.text(&score_text, TextConfig::new().font_size(24).color(COLOR).end());
}
// After this function returns, Clay may still try to use the pointer, causing undefined behavior

Current Implementation:

clay/src/lib.rs

Lines 550 to 559 in 6db8ab1

impl From<&str> for Clay_String {
fn from(value: &str) -> Self {
Self {
// TODO: Can we support &'static str here?
isStaticallyAllocated: false,
length: value.len() as _,
chars: value.as_ptr() as _,
}
}
}

This works for string literals (&'static str), but not for dynamic strings, which are freed after the function returns.

Problem:

  • a dangling pointer is created
  • use-after-free bugs, memory corruption, and crashes
  • lifetimes cannot be enforced across the bindings

Workarounds:

  • leaking memory (Box::leak)
  • managing a buffer of strings

Proposed Solution:

  • If not 'static, copy the string data internally
  • Free when no longer required
  • No use-after-free bugs
  • Existing usages would still work

This change would make the bindings much safer and easier to use for dynamic UI text.

Would you accept a PR that copies non-static string data internally?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions