Android 17: bug en libc rompe apps con emoji y CJK

Un bug de Android 17 que solo aparece cuando el texto no está en inglés

En los builds de Android 17 de septiembre de 2026, dos funciones de bionic, la librería C del runtime de Android, empiezan a fallar en cuanto el texto deja de ser ASCII. wcscspn() y wcspbrk() tratan como coincidencia cualquier carácter con código igual o superior a U+0100, sin importar cuál sea el conjunto de caracteres que se está buscando.

El alcance es enorme: un barrido de todos los puntos de código, desde U+0001 hasta U+10FFFF, determinó que el fallo se dispara en 1.111.808 casos, todos los caracteres del rango U+0100–U+10FFFF. Emojis, chino, japonés, coreano, cirílico, griego y latín extendido caen adentro.

Para una app que recorre cadenas anchas buscando delimitadores ASCII —un salto de línea, un asterisco, un signo de interrogación— el efecto no es cosmético. El código corta la cadena en la posición equivocada y reporta un error. Basta con que un comentario en un archivo tenga un emoji.

¿Y esto cómo se aplica en tu negocio?

En CAR, dueños de empresa de Latinoamérica y España usan la tecnología para ser más rentables. Empiezas con una ruta de 7 días y terminas con un sistema funcionando en tu negocio.

👥 Probar 7 días

¿Qué se rompe exactamente y en qué condiciones?

No todas las llamadas fallan. El bug aparece cuando el conjunto de caracteres rechazados tiene dos o más elementos, todos por debajo de U+0100. Con un solo carácter, la función va bien. Si al menos un miembro del conjunto es igual o mayor a U+0100, también. Y si el texto escaneado se limita al rango U+0080–U+00FF, no hay problema.

Los casos reportados como afectados son un Pixel 7 y un Pixel 8a, ambos con el build CP3A.260905.009 y parche de seguridad del 5 de septiembre de 2026. Un Nothing Phone 2 con Android 16 y parche del 1 de agosto de 2026 no presenta el fallo, lo que apunta a que es específico de Android 17.

También vale saber qué no está roto: wcsspn(), wcschr(), wcsrchr(), wmemchr() y wcsstr() se comportan correctamente.

¿Por qué nadie lo detectó antes de que llegara a los dispositivos? Porque el código que realmente corre en esos builds no está publicado. Las fuentes públicas de android-17.0.0r1, de junio de 2026, construyen wcscspn y wcspbrk desde external/llvm-libc, con un bucle anidado simple sobre wchart que es correcto. El código de los builds de septiembre hace otra cosa: la regresión se introdujo en algún punto entre junio y septiembre.

El mecanismo, reconstruido desarmando libc.so

Sin acceso a la fuente, el autor del reporte desarmó /apex/com.android.runtime/lib64/bionic/libc.so —la librería que Google efectivamente distribuye en el dispositivo— y describió tres caminos según la forma del conjunto:

  1. Un solo carácter: comparación directa. Correcto.
  2. Dos o más caracteres: se llena una tabla de 256 bytes en el stack mientras los caracteres del conjunto estén en el rango 1–0xFF. Si aparece uno mayor o igual a 0x100, la función cae a llamar wcschr() por cada carácter. Correcto.
  3. En el resto de los casos, el texto se escanea contra esa tabla.

La falla está en ese tercer camino: cuando el carácter leído es mayor o igual a 0x100, el código salta a la etiqueta que detiene el escaneo y lo reporta como coincidencia. Un carácter de ese rango no puede pertenecer a un conjunto cuyos miembros son todos menores a 0x100, así que wcscspn() debería seguir buscando. Deja de buscar.

wcspbrk() hereda el problema porque está implementada sobre wcscspn(). Y wcsspn(), que comparte la misma estructura, no está mal: en las funciones de complemento, detenerse ante un carácter que no pertenece al conjunto es justamente la respuesta correcta. Eso explica por qué solo dos de las siete funciones evaluadas fallan.

El test que ya existía estaba mal escrito

bionic tiene un test, TEST(wchar, wcspbrk), que solo usa entradas ASCII, así que nunca podía detectar este caso. Un mantenedor de Google confirmó en el hilo que el bug sigue reproduciéndose en la punta del árbol de desarrollo, que agregará los dos tests faltantes y que intentará hacer backport del arreglo para reducir la cantidad de dispositivos afectados.

En el mismo comentario reconoció algo más incómodo: el test existente para wcscspn() también está mal, con una expectativa de 2 donde el valor correcto era 3. La red de seguridad no solo tenía un agujero: además tenía un dato equivocado adentro.

¿Cómo se manifestó en el mundo real?

El caso más documentado es Visual Pinball en Android, a través de libwinevbs, una compilación Unix del motor VBScript de Wine. Después de la actualización de septiembre dejó de cargar cualquier mesa cuyo código VBScript incluyera un comentario con un emoji: el lexer usa wcspbrk() para saltar comentarios hasta el fin de línea, obtuvo la posición del primer emoji y reportó un carácter inválido.

El mismo problema afectó a FileSystem.MoveFolder, que pasó a tratar como patrón comodín cualquier ruta con un carácter igual o superior a U+0100. La solución llegó desde afuera del sistema operativo: reemplazar las llamadas por bucles propios, un cambio ya publicado en el proyecto.

El problema de fondo: el código que corre no siempre está publicado

El titular de esta historia no es el bug, es cómo se reporta. El autor tuvo que desarmar una librería compartida porque la rama con el código correspondiente no estaba disponible, y lo dijo sin rodeos en el hilo: resulta desafortunado que la fuente no esté accesible todavía.

Otro desarrollador llevó la discusión al plano de la gobernanza: sostiene que, desde que Google decidió cerrar el código fuente de AOSP, proliferan problemas sin arreglo y sin transparencia sobre cómo se gestiona el proyecto. Un colaborador de Google respondió que el sitio oficial source.android.com concentra la comunicación sobre el tema y aclaró el enredo de nombres: android-latest-release es una rama de manifiesto, mientras que android17-release es la rama de git, y las fuentes de bionic de Android 17 sí están publicadas en el repositorio de AOSP.

Un tercer contribuyente preguntó directamente dónde está el código de Android 17 QPR1, el que corre en los Pixel y lleva más de un mes estable. La respuesta implícita apareció en otro comentario: la rama android-latest-release todavía es demasiado vieja para incluir cambios de abril, por eso el consejo de mirar el código fuente no aplicaba en este caso.

¿Qué significa esto para tu startup?

  • Escribe tests con datos que no sean ASCII. Si tu suite solo alimenta cadenas en inglés, esta clase de regresión es invisible para ti. Una aserción con un texto que contenga un carácter por encima de U+0100 y un salto de línea alcanza para cubrir el caso.
  • Audita quién escanea cadenas anchas en tus dependencias. Los sospechosos típicos son lexers, motores de scripting embebidos, parsers de rutas y cualquier código que compare contra salto de línea, retorno de carro o comodines.
  • Ten un workaround a mano. El equipo de Visual Pinball resolvió el problema con bucles propios en lugar de esperar un parche del sistema operativo. Si tu producto depende de una función del sistema y no puedes actualizar el dispositivo del usuario, el reemplazo local es la única salida rápida.
  • No asumas que el binario del dispositivo se parece al código que lees. Prueba en builds reales y en el rango de dispositivos que soportas, no solo contra las fuentes públicas.
  • Si tu producto recibe contenido generado por usuarios, el disparador ya está en tu base. Comentarios, nombres con emoji, texto importado: cualquiera de esos campos puede activar el fallo en un dispositivo con Android 17.
  • Si compilas con NDK, revisa la versión. El formulario del reporte marca la r29 como la afectada.

Conclusión

Un emoji dentro de un comentario alcanzó para romper una cadena de herramientas completa. La lección para un equipo de producto es doble: los tests con datos poco habituales no son un lujo, son la única forma de cubrir a los usuarios que no escriben en inglés, y ninguna dependencia del sistema operativo es un contrato garantizado cuando el código que corre en el dispositivo va por delante del código que se publica. La reacción más útil no es esperar el parche, sino aislar la dependencia y tener un reemplazo propio listo para activar.

Fuentes

¿Y esto cómo se aplica en tu negocio?

En CAR, dueños de empresa de Latinoamérica y España usan la tecnología para ser más rentables. Empiezas con una ruta de 7 días y terminas con un sistema funcionando en tu negocio.

👥 Probar 7 días

Daily Shot: Tu ventaja táctica

Lo que pasó en las últimas 24 horas, resumido para que tú no tengas que filtrarlo.

Suscríbete para recibir cada mañana la curaduría definitiva del ecosistema startup e inversionista. Sin ruido ni rodeos, solo la información estratégica que necesitas para avanzar:

  • Venture Capital & Inversiones: Rondas, fondos y movimientos de capital.
  • IA & Tecnología: Tendencias, Web3 y herramientas de automatización.
  • Modelos de Negocio: Actualidad en SaaS, Fintech y Cripto.
  • Propósito: Erradicar el estancamiento informativo dándote claridad desde tu primer café.

📡 El Daily Shot Startupero

Noticias del ecosistema startup en 2 minutos. Gratis, todos los días.

Share to...