Skip to content

ICU-23359 Fix use-after-free race in TransliteratorAlias compoundFilter#3913

Open
hirorogo wants to merge 1 commit intounicode-org:mainfrom
hirorogo:fix-transreg-compoundfilter-race
Open

ICU-23359 Fix use-after-free race in TransliteratorAlias compoundFilter#3913
hirorogo wants to merge 1 commit intounicode-org:mainfrom
hirorogo:fix-transreg-compoundfilter-race

Conversation

@hirorogo
Copy link
Copy Markdown

@hirorogo hirorogo commented Mar 19, 2026

Checklist

  • Required: Issue filed: ICU-23359
  • Required: The PR title must be prefixed with a JIRA Issue number. Example: "ICU-NNNNN Fix xyz"
  • Required: Each commit message must be prefixed with a JIRA Issue number. Example: "ICU-NNNNN Fix xyz"
  • Issue accepted (done by Technical Committee after discussion)
  • Tests included, if applicable
  • API docs and/or User Guide docs changed or added, if applicable
  • CLA signed

Summary

TransliteratorAlias stored a raw non-owning pointer to the TransliteratorEntry's compoundFilter. When the registry mutex is released between registry->get() and alias->create(), a concurrent Transliterator::unregister() call can delete the entry (and its compoundFilter) while the alias still holds a dangling pointer, causing a use-after-free.

The source comment at transreg.cpp lines 30-36 notes that BoundsChecker has reported dangling pointer errors with these entry objects, corroborating this race condition.

Changes

  • Clone compoundFilter in both TransliteratorAlias constructors instead of storing a raw alias pointer
  • Delete owned compoundFilter in ~TransliteratorAlias()
  • Update header comment and type from const UnicodeSet* (alias) to UnicodeSet* (owned)
  • Add TestTransliteratorAliasCompoundFilterRace in tsmthred.cpp to exercise concurrent createInstance() + unregister() with a compound-filter transliterator

Test plan

  • Existing ICU transliterator tests pass
  • Concurrent createInstance + unregister on the same ID no longer triggers UAF (new test: TestTransliteratorAliasCompoundFilterRace)

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 19, 2026

CLA assistant check
All committers have signed the CLA.

@markusicu
Copy link
Copy Markdown
Member

@hirorogo Could you please sign the CLA -- otherwise we can't look at your changes -- and create a Jira ticket with the problem description?

hirorogo added a commit to hirorogo/icu that referenced this pull request Mar 27, 2026
@hirorogo hirorogo force-pushed the fix-transreg-compoundfilter-race branch from b2507fc to b8391c6 Compare March 27, 2026 14:20
@jira-pull-request-webhook
Copy link
Copy Markdown

Hooray! The files in the branch are the same across the force-push. 😃

~ Your Friendly Jira-GitHub PR Checker Bot

@hirorogo hirorogo force-pushed the fix-transreg-compoundfilter-race branch from b8391c6 to 9a89dab Compare March 27, 2026 14:21
@jira-pull-request-webhook
Copy link
Copy Markdown

Hooray! The files in the branch are the same across the force-push. 😃

~ Your Friendly Jira-GitHub PR Checker Bot

hirorogo added a commit to hirorogo/icu that referenced this pull request Mar 27, 2026
@hirorogo hirorogo force-pushed the fix-transreg-compoundfilter-race branch from 9a89dab to 856bf9c Compare March 27, 2026 14:22
@jira-pull-request-webhook
Copy link
Copy Markdown

Hooray! The files in the branch are the same across the force-push. 😃

~ Your Friendly Jira-GitHub PR Checker Bot

@hirorogo
Copy link
Copy Markdown
Author

hirorogo commented Apr 3, 2026

I've restored the PR template, added a unit test (TestTransliteratorAliasCompoundFilterRace), and the CLA is signed. Could you please create a Jira ticket for this issue? I'll update the PR title and commit message with the ticket ID once assigned. Thank you!

@hirorogo hirorogo changed the title Fix use-after-free race in TransliteratorAlias compoundFilter ICU-23359 Fix use-after-free race in TransliteratorAlias compoundFilter Apr 5, 2026
@hirorogo hirorogo force-pushed the fix-transreg-compoundfilter-race branch from 76d69f2 to fcf4324 Compare April 5, 2026 05:12
hirorogo added a commit to hirorogo/icu that referenced this pull request Apr 5, 2026
TransliteratorAlias::compoundFilter was stored as a raw alias to
the caller-owned filter, leading to use-after-free when the caller
destroys its copy. Clone the filter into an owned instance instead.

Adds concurrency test exercising concurrent createInstance() +
unregister() with a compound filter to verify the fix.

See unicode-org#3913
@jira-pull-request-webhook
Copy link
Copy Markdown

Hooray! The files in the branch are the same across the force-push. 😃

~ Your Friendly Jira-GitHub PR Checker Bot

Copy link
Copy Markdown

@qdan8556-glitch qdan8556-glitch left a comment

Choose a reason for hiding this comment

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

@hirorogo
Copy link
Copy Markdown
Author

hirorogo commented Apr 6, 2026

@markusicu CLA is signed, and I've created the Jira ticket: ICU-23359. The PR title and commit message are updated with the ticket ID.

A unit test (TestTransliteratorAliasCompoundFilterRace) is included in tsmthred.cpp — it exercises concurrent createInstance() + unregister() with a compound-filter transliterator.

Could the Technical Committee please accept ICU-23359 when you get a chance? The jira-ticket CI check is currently failing because the ticket status is still "New".

Thank you!

Copy link
Copy Markdown
Author

@hirorogo hirorogo left a comment

Choose a reason for hiding this comment

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

(replaced — see English review below)

Copy link
Copy Markdown
Author

@hirorogo hirorogo left a comment

Choose a reason for hiding this comment

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

Reviewed the changes. The fix correctly addresses the root cause — the ownership ambiguity of compoundFilter.

Before this patch, TransliteratorAlias held a raw non-owning pointer to the entry's compoundFilter. Once the registry mutex is released between get() and create(), a concurrent unregister() can delete the entry and leave the alias with a dangling pointer. Cloning in the constructor and taking ownership eliminates this dependency entirely.

A few minor observations:

  1. static_cast<UnicodeSet*>(compoundFilter->clone()) — since clone() returns UnicodeFilter*, a dynamic_cast with a null check would be slightly more defensive. That said, the original code did the same, so this is pre-existing.

  2. No handling if clone() returns nullptr on OOM. Looks consistent with other ICU code, just noting it.

  3. The test's success criterion is "no crash / no sanitizer report", which means it may pass silently without ASan/TSan. Fine if CI runs sanitizer builds.

None of these are blockers.

@richgillam richgillam self-assigned this Apr 9, 2026
@@ -105,14 +105,14 @@ class TransliteratorAlias : public UMemory {
// Here ID is the ID, aliasID is the idBlock, trans is the
// contained RBT, and idSplitPoint is the offset in aliasID
// where the contained RBT goes. compoundFilter is the
// compound filter, and it is _not_ owned.
// compound filter, and it is owned (cloned from entry).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: Reword comment to "(cloned from input)", with "input" being clearer than "entry" as referring to the ctor's input parameter.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — updated to "(cloned from input)".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — updated to "(cloned from input)".

@hirorogo hirorogo force-pushed the fix-transreg-compoundfilter-race branch from 3fda95b to 7c545c5 Compare April 10, 2026 07:07
@jira-pull-request-webhook
Copy link
Copy Markdown

Hooray! The files in the branch are the same across the force-push. 😃

~ Your Friendly Jira-GitHub PR Checker Bot

Copy link
Copy Markdown
Contributor

@poulsbo poulsbo left a comment

Choose a reason for hiding this comment

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

Looks good, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants