deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Android 17 bionic bug makes wcscspn and wcspbrk match every wide character above U+00FF

A regression in September 2026 Android 17 builds makes the bionic wide-string functions wcscspn() and wcspbrk() report a false match on every character from U+0100 up. The cause was pinned down by disassembling the shipped libc.

Android 17 bionic bug makes wcscspn and wcspbrk match every wide character above U+00FF

The bug

A bug report filed on Google's Android NDK issue tracker, which also reached the Hacker News front page, documents a regression in Android 17's C library, bionic. On September 2026 builds (CP3A.260905.009) — confirmed on a Pixel 7 and a Pixel 8a — the wide-character functions wcscspn() and wcspbrk() report a match on any character of U+0100 or higher, as long as the reject set contains two or more characters that all sit below U+0100. A Nothing Phone 2 still running Android 16 behaves correctly, so the problem is specific to the newer release.

The report includes a compact reproducer. Calling wcscspn() with a reject set of L"x\n" against a string holding 'a', an emoji (U+1F680), U+0100, 'b', 'x' and a newline returns 1 instead of 4: the scan halts on the emoji and treats it as a hit. A sweep across nearly the entire Unicode range found that every code point from U+0100 through U+10FFFF — roughly 1,111,808 characters, excluding surrogates and the set members — is mishandled, while everything from U+0001 to U+00FF works.

The shape of the set decides whether the bug appears. Single-character sets are safe, and sets containing at least one character at or above U+0100 are also safe because that path falls back to wcschr(). Only multi-character sets composed entirely of low values trigger it. The related functions wcsspn(), wcschr(), wcsrchr(), wmemchr() and wcsstr() all behave correctly.

Found by disassembling the shipped library

The most striking part of the story is how the cause was established. According to the report, the public Android 17 source drop (android-17.0.0_r1, from June 2026) builds wcscspn and wcspbrk from external/llvm-libc as a plain nested loop over wchar_t, which is correct. That means the regression was introduced somewhere between the June source snapshot and the September device builds, in code that has not yet been published.

With no matching source available, the reporter disassembled the shipped /apex/com.android.runtime/lib64/bionic/libc.so. The disassembly shows three code paths selected by the set: a simple compare loop for single-character sets, a wcschr()-based fallback when a set member reaches U+0100, and — for everything else — a 256-entry boolean table built on the stack. In that last path, the scan loop checks each character against the table, but when it loads a character at 0x100 or above it takes a branch that ends the scan and reports a match. A character that large cannot possibly belong to a set whose members are all below U+0100, so a correct wcscspn must keep going. wcsspn shares the same structure, but for that function stopping happens to be the right answer, which explains why only the complement functions are wrong; wcspbrk is implemented on top of the same scan and inherits the defect.

Real-world fallout and a testing gap

The report ties the bug to concrete breakage in Visual Pinball on Android, whose libwinevbs component is a Unix build of Wine's VBScript engine. After the September update, the app stopped loading any table whose VBScript source contained an emoji inside a comment: the lexer skips comments using wcspbrk with a newline/carriage-return set, received the emoji's position instead of the end of the line, and raised an "Invalid character" error. FileSystem.MoveFolder had a similar failure, treating any path containing a character at or above U+0100 as a wildcard pattern. The project shipped its own workaround, replacing the calls with hand-written loops.

Google's own tests missed the bug because the existing bionic wcspbrk test uses ASCII input only. The report notes that a case such as checking wcscspn(L"aĀb\n", L"x\n") would have caught it immediately. The issue was filed against NDK r29, which is currently supported.

Why it matters

Any native Android code that scans wide strings for ASCII delimiters — newlines, wildcards, separators — will silently misparse the moment the text contains an emoji or a CJK, Cyrillic, Greek or Latin Extended character, which is to say ordinary user-generated content. The bug is invisible to ASCII-only tests, so apps may crash or corrupt parsing in ways that only surface in production.

The episode also illustrates the practical cost of AOSP's delayed source releases. A contributor who wanted to file a useful, one-line-diagnosable bug report had to reverse-engineer a system binary because the published tree does not match what ships on devices. Until Google closes that gap — or fixes the branch condition — developers targeting Android 17 have to treat these standard library functions as suspect.

  • #android
  • #bionic
  • #libc
  • #unicode
  • #ndk
  • #aosp

Related posts