Skip to content

Replacing smartstring which is unmaintained, has UB and a restrictive license #816

Closed
2 of 2 issues completed

Description

@MolotovCherry

In part of my miri testing of Rhai, I've discovered that the smartstring crate is unsound in general. After spending many hours tracking down the cause of the UB, I finally narrowed it down and found an issue report for the exact UB. The example I posted also introduces the same UB in Rhai when using the ImmutableString type. (Though it's not limited to my example)

I don't know what the exact unsound conditions for the UB are, but I'm sure someone has accidentally triggered it in Rhai (as I have as well).

The same UB mentioned in the issue below can also be triggered with a var.into() for ImmutableString in Rhai.

Since the last update for this crate was ~2 years ago, I have some doubts on whether the UB will ever be fixed. (Edit: Now archived, so definitely confirmed unmaintained)
bodil/smartstring#49

Note, there IS a PR with the fix as far as I can tell, but the author seems to have been unresponsive to issues/PRs. This could be quickly patched by cloning the repo and using that one instead however

For any people who see this and want to fix the potential UB, see this PR for the fix, clone the repo and commit the fix, then place the following lines in your Cargo.toml:

[patch.crates-io]
smartstring = { git = "URL to your patched git repo" }

Activity

  1. schungx commented on Jan 18, 2024

    @schungx
    Collaborator

    There is a way to use a GitHub PR in Cargo.toml...

    [patch.crates-io]
    smartstring = { git = "https://github.com/bodil/smartstring", ref = "refs/pull/34/head" }
  2. passcod commented on Feb 25, 2026

    @passcod

    Just to avoid a headscratcher, it's /ref =/rev =/:

    [patch.crates-io]
    smartstring = { git = "https://github.com/bodil/smartstring", rev = "refs/pull/34/head" }

    Thinking of swapping out smartstring, whether for another crate or for a fork, maybe doing something like

    # Cargo.toml
    
    [features]
    safe-smartstring = ["dep:some-smartstring-fork"]
    #[cfg(feature = "safe-smartstring")]
    pub use some_smartstring_fork::SmartString;
    
    #[cfg(not(feature = "safe-smartstring"))]
    pub use smartstring::SmartString;

    could let people opt-in to a safer implementation without a breaking change to the project, and without too much churn, and then if 2.0 appears one day, remove the old smartstring and be sound by default.

  3. schungx commented on Feb 25, 2026

    @schungx
    Collaborator

    Thinking of swapping out smartstring, whether for another crate or for a fork, maybe doing something like

    Yeah, I have been thinking about it, but smartstring has been working well so far with no particular issues. And unfortunately the SmartString type has been exposed in Rhai's public interface, so it is going to be a breaking change even if we move to a completely compatible crate...

    And I haven't found one that works as well, except perhaps for ecow, but not sure how matured that one is...

  4. changed the title [-]smartstring is unsound and has UB[/-] [+]Replacing smartstring which is unmaintained, has UB and a restrictive license[/+] on Aug 12, 2026
  5. pinned this issue on Aug 12, 2026
  6. schungx commented on Aug 12, 2026

    @schungx
    Collaborator

    OK, we'll continue the smartstring discussion on this issue, which is the first one raised.

    Any suggestions on which crate to replace it with? How about compact_str?

  7. thalesfragoso commented on Aug 12, 2026

    @thalesfragoso

    There is a way to use a GitHub PR in Cargo.toml...

    [patch.crates-io]
    smartstring = { git = "https://github.com/bodil/smartstring", ref = "refs/pull/34/head" }

    I don't think you will be able to do a release to crates.io with that kind of dependency, usually crates.io requires all deps to be available through crates.io too.

  8. 6 remaining items

  9. schungx commented on Sep 27, 2026

    @schungx
    Collaborator

    The only public-facing change is the key type of rhai::Map, so if you'd prefer to land it in 2.0 rather than 1.x, I'm happy to retarget it.

    Yes, this is my main concern. There are a number of public APIs with impl Into<SmartString> so there is no telling how much code it'd break.

    Also we can consider changing SmartString (a type alias defined by Rhai) to compact_str... That may keep the changes minimal...

    EDIT: Oh, mah bad.SmartString is an internal type alias, not exported.

    Moving to 2.0 would be the safe choice. But then, since we're 2.0, we can consider other small breaking changes as well... Or maybe larger rearchitecture of the codebase...

    the refcount is always atomic (also in non-sync builds

    My concerns with ecow is exactly this: the Arc. Using it as a map key type will not affect things much as using SmartString would also be an allocation, but not ref counted.

    The main concern is that ImmutableString is a single machine word right now, which is a shame if ecow is used and it bumps to two words... That would potentially make a lot of data types larger...

  10. hoppworks commented on Sep 28, 2026

    @hoppworks
    Contributor

    Thanks, that makes sense. And agreed on ecow: with ImmutableString at one word today, going to 16 bytes would grow every type that embeds it (Dynamic included), so that's not a good trade. I'd take ecow off the table for ImmutableString.

    On how much the impl Into<Identifier> APIs would break, I compared the From impls. SmartString<LazyCompact> can be built from &str, &mut str, String, &String, Box<str> and Cow<str>. CompactString covers all of those except &mut str. The From<ImmutableString> / From<&ImmutableString> / From<Token> impls are Rhai's own and move over in #1175. So on those parameters, only callers passing a &mut str would stop compiling. The more visible change remains the key type of rhai::Map for code that names smartstring::SmartString explicitly.

    Still, if you'd rather keep 1.x strictly stable, I'm happy to retarget #1175 to a 2.0 branch whenever there is one. Just let me know.

  11. schungx commented on Sep 28, 2026

    @schungx
    Collaborator

    I suggest gating it under a new feature flag so that people with license restrictions can start using it.

    In 2.0 we can remove the flag and make it the new default.

  12. hoppworks commented on Sep 28, 2026

    @hoppworks
    Contributor

    Sounds good, done. #1175 now adds an opt-in compact_str feature. Without it nothing changes; with it, Identifier and the key type of Map become CompactString. MSRV stays at 1.66, and there's a new compact_str row in the CI build matrix.

    Note that smartstring remains a regular dependency until 2.0 (Cargo can't drop a dependency when a feature is enabled), so cargo deny/cargo audit will still list it in Cargo.lock until then.

  13. wensimehrp commented on Sep 28, 2026

    @wensimehrp

    so if you'd prefer to land it in 2.0 rather than 1.x, I'm happy to retarget it.

    Just let me know.

    I'm sorry, but @hoppworks are you using AI to generate all the comments and the PR description???

  14. hoppworks commented on Sep 28, 2026

    @hoppworks
    Contributor

    Yes, I use AI to turn voice notes into written comments and to help edit the wording, including the PR description. The technical points are what I wanted to communicate, and I’m responsible for checking them and for the changes in the PR.

    I can see why the polished wording made you ask. If anything I’ve written is unclear or technically wrong, please point it out and I’ll address it directly.

  15. schungx commented on Sep 28, 2026

    @schungx
    Collaborator

    Nothing wrong with AI in my opinion. Just a tool which works fine if used well.

  16. linked a pull request that will close this issueReplace smartstring with compact str #1175on Sep 28, 2026
  17. wensimehrp commented on Sep 28, 2026

    @wensimehrp
  18. hoppworks commented on Sep 28, 2026

    @hoppworks
    Contributor

    I get that and AI does have a weird tone...

  19. unpinned this issue on Sep 30, 2026
  20. marked smartstring unmaintained #1186 as a duplicate of this issue on Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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