Linux elimina la API `strncpy` tras 6 años y más de 360 parches
(phoronix.com/news)- En Linux 7.2 desaparecen los usos de la API
strncpydentro del kernel, y la interfaz de copia de cadenas, cuyo retiro estaba previsto desde hace tiempo, queda eliminada por completo strncpy()copia la cantidad de bytes indicada, pero su comportamiento de terminación NUL no es intuitivo, por lo que durante años siguió siendo una fuente de errores en el kernel- Su característica de rellenar innecesariamente con 0 el búfer de destino incluso generaba problemas de rendimiento, y quitarla requirió cerca de 6 años y 362 commits
- En el merge del viernes se eliminó no solo la API en sí, sino también la última implementación específica por arquitectura para per-CPU
- El código del kernel ahora debe elegir funciones alternativas según el caso de uso, como
strscpy(),strscpy_pad(),strtomem_pad(),memcpy_and_pad()ymemcpy()
strncpy desaparece en Linux 7.2
- Linux 7.2 elimina por completo la API
strncpy, cuyo retiro dentro del kernel estaba previsto desde hace mucho tiempo - Tras 6 años de trabajo de limpieza, ya no queda código dentro del kernel que use la interfaz
strncpy - Este cambio no fue solo un reemplazo de función, sino más bien una limpieza de prácticas antiguas de copia de cadenas en todo el kernel
La magnitud del trabajo hasta su eliminación
- La eliminación de
strncpyrequirió unos 362 commits - El trabajo avanzó eliminando de forma gradual el código del kernel que usaba
strncpy - En Linux 7.2 esta limpieza finalmente llegó a su punto de cierre
Por qué strncpy era un problema en el kernel
- Durante años,
strncpyfue considerada una fuente constante de errores dentro del kernel de Linux - En particular, había dos comportamientos problemáticos
- El significado y comportamiento de la terminación NUL no eran intuitivos, por lo que era fácil equivocarse al usarla
- Rellenaba con 0 el búfer de destino de forma redundante, generando un costo de rendimiento innecesario
El merge que concretó la eliminación
- El viernes se realizó este merge, que eliminó la API
strncpy - En el mismo merge también desapareció la última implementación de
strncpyespecífica por arquitectura para per-CPU
APIs alternativas para usar en el código del kernel
- En lugar de
strncpy, ahora hay que elegir la función adecuada según el destino de copia y la condición de terminaciónstrscpy(): para destinos con terminación NULstrscpy_pad(): para destinos con terminación NUL cuando se necesita padding con 0strtomem_pad(): para campos de ancho fijo sin terminación NULmemcpy_and_pad(): para copias limitadas con padding explícitomemcpy(): para copias de memoria cuando se conoce la longitud
1 comentarios
Comentarios en Hacker News
Antes se burlaban de que los desarrolladores del kernel de Linux, supuestamente entre los mejores programadores en C del mundo, no sabían crear tipos como stringbuffer o stringview, pero en esa época no existía el nivel de consenso que hay ahora sobre este tema, así que se entiende hasta cierto punto
Quien ya había visto la dirección correcta era Dennis Ritchie, que en 1990 propuso un tipo de puntero gordo para C. Habría sido una adición perfecta si hubiera entrado en C99, y si el comité lo hubiera incluido, el mundo quizá sería bastante distinto
En 2007 hubo una segunda oportunidad con el artículo “C's greatest mistake” de Walter Bright, que explicaba con más claridad la idea de slices/stringview, esencialmente la misma de Ritchie, pero tampoco logró entrar en C11. Ya llegamos a C23 y sigue sin existir; en cambio obtuvimos _Generic y VLA, así que al parecer toca hacer fiesta
Buscando también vi un post de Reddit sobre el mismo tema, y la discusión tipo bike-shedding estuvo graciosa: https://www.reddit.com/r/C_Programming/comments/90uq7c/cs_bi...
Me intriga por qué se diseñó así el comportamiento por el que los arreglos en C se degradan a punteros. Hay una explicación de que el objetivo era poder compilar código de B a C con cambios mínimos; al parecer, en B la declaración de arreglos en realidad definía tanto un puntero como un arreglo, e inicializaba ese puntero para que apuntara al primer elemento del arreglo
El problema más grande ahora es que la biblioteca estándar de C sigue atada a la era de K&R, y ni siquiera funciones del lenguaje añadidas en C99, como pasar o devolver structs, se han reflejado en la API de la biblioteca estándar. Bastaría con que la biblioteca estándar tuviera structs de rango de puntero/tamaño y funciones de cadenas nuevas o actualizadas que los usaran para que la situación mejorara bastante
Dicen que strncpy dentro del kernel de Linux fue durante años una “fuente persistente de bugs” por su semántica contraintuitiva, el manejo de terminación NUL y el costo de rendimiento de rellenar el destino con ceros innecesariamente
Cada vez que me pedían revisar código en C, buscaba strncpy, y siempre encontraba un bug ahí
Hay cosas que me han irritado durante 40 años. Las cadenas terminadas en NUL, y ahora también las cadenas que no son UTF-8 en entrada/salida
También la convención de tratar el fin de línea como LF, CR o CRLF, y la forma de separar campos con pipes o comas. Si se hubieran usado caracteres ASCII no ambiguos como GS, FS y RS, la codificación/decodificación de fin de línea sería un problema de E/S, y HT/VT/CR/LF/FF podrían haberse quedado literalmente como códigos relacionados con la salida
Desaparece todo el problema sucio del escape que aparece con datos separados por comas, así que todo se vuelve mucho más simple
El estándar Unicode dice que no solo CR, LF, CRLF y esos caracteres, sino también tabulación vertical y salto de página, deben tratarse como separadores de línea
Los finales de línea como LF, CR y CRLF también son una convención del sistema operativo, y es mejor que los lenguajes de programación no intenten “adivinar” cuál es el final de línea correcto. Eso crea más problemas de los que resuelve y, de nuevo, en general es un problema muy de Windows, así que Microsoft tendría que traer Windows al siglo actual
La última vez que tuve que manejar un archivo CSV en bash, internamente lo convertí a RS y FS para procesarlo
En lugar de strncpy, dicen que en el código del kernel de Linux hay que usar strscpy() para destinos terminados en NUL, strscpy_pad() para destinos terminados en NUL que requieren padding con ceros, strtomem_pad() para campos de ancho fijo no terminados en NUL, memcpy_and_pad() para copias delimitadas con padding explícito, y memcpy() para copias de memoria de longitud conocida
Esto suena a pesadilla, y no sé si realmente tiene que ser tan complejo
Creo que es mejor que, al leer el código, la intención quede clara solo con la función elegida
Justo en este tipo de trabajo repetitivo y tedioso es donde ocurre el verdadero trabajo de la ingeniería de sistemas
Estos grandes proyectos de infraestructura, que vuelven más confiable al kernel de Linux mientras lo mantienen utilizable durante todo el proceso, no se mueven en escalas de meses sino de décadas
Pero no sé si a esa velocidad se puede lograr un progreso significativo a largo plazo. No es tanto una queja como una paradoja de la infraestructura crítica
Es un trabajo impresionante y que te vuelve humilde. Sorprende que tanta gente haya contribuido a esto
Las “nuevas funciones geniales” suelen recibir reconocimiento fácilmente, pero en algo tan fundamental como el kernel, quitar funciones malas puede ser todavía más importante
Cuando dentro de 50 años llegue una era en la que la gente haya olvidado cómo leer código fuente, mientras se acumulan silenciosamente los residuos de Claude/Codex y se quema la mayor parte de la energía de la Tierra, da la impresión de que este tipo de cosas quedarán como leyendas de la “era fundacional”
Y además es la única persona que sabe qué es el Unix epoch
Creo que las cadenas terminadas en 0 fueron el mayor error en la historia de la computación. Las cadenas al estilo Pascal eran mucho más seguras
Sigue siendo un puntero a un arreglo de caracteres terminado en 0, pero hay un campo de longitud justo antes del primer byte al que apunta el puntero. Asumiendo que no hay NUL embebidos, también es compatible con cadenas C, y las funciones del tipo BSTR pueden aprovechar el valor de longitud
Durante un tiempo, incluso 16 bits pudieron parecer excesivos, y ahora 32 bits pueden parecer demasiado pocos. C, que se presenta como un lenguaje de “tipado fuerte”, en los lugares importantes en realidad es bastante laxo
Hace más de 30 años que no escribo código relacionado con Pascal, pero recuerdo vagamente que incluso entonces me parecía que su sistema de cadenas era demasiado incómodo de usar
Hay demasiado dolor y demasiado trabajo inútil por no tener ni siquiera un tipo de dato de cadena
strncpyuse ese tipo y esas funciones, ¿no?Me pregunto qué tuvo de tan difícil reescribir los usos de
strncpycomo para que haya tomado 6 añosMe gustaría saber si era porque se usaba en todas partes, o si fue un trabajo de largo plazo en el que solo lo cambiaban cuando ya tocaba modificar el mismo archivo, o si hubo alguna otra dificultad
Me tocó lidiar con código en una app de Win32 que usaba cadenas rellenadas con espacios. La cadena de destino se rellenaba con espacios, pero el último byte seguía siendo un terminador nulo
Para cosas como longitud y copiado, había que usar versiones especiales de las funciones de cadena. No sé por qué era así, pero como el codebase era tan viejo, quizá se originó en el comportamiento de estructuras de Pascal