11 puntos por GN⁺ 2026-01-01 | 5 comentarios | Compartir por WhatsApp
  • El proyecto cURL, después de haber eliminado antes strncpy(), ahora también prohíbe por completo strcpy() en su base de código
  • strcpy() tiene una API simple, pero existe el riesgo de separar la validación del tamaño del búfer, lo que la vuelve insegura en el mantenimiento a largo plazo
  • Como reemplazo, se introdujo una nueva función llamada curlx_strcopy(), que recibe como argumentos tanto el tamaño del búfer de destino como la longitud de la cadena para verificar si la copia es posible antes de ejecutarla
  • Esta función usa internamente memcpy() y también garantiza el manejo del carácter nulo de terminación
  • Con este cambio se busca mejorar la seguridad y la consistencia del código, además de reducir el problema de que la IA genere reportes erróneos de vulnerabilidades

Contexto de la eliminación de strcpy

  • cURL ya había eliminado en el pasado todas las llamadas a strncpy(), señalando los problemas de esta función: API poco intuitiva, falta de garantía de terminación nula y relleno innecesario con ceros
    • En los casos donde se necesita copiar subcadenas, se cambió a usar memcpy() y manejar la terminación nula manualmente
  • strcpy() tiene una API simple, pero no especifica el tamaño del búfer, así que durante el mantenimiento existe el riesgo de que el código de validación y la llamada de copia queden separados
    • Si el código es modificado durante décadas por distintos desarrolladores, existe la posibilidad de que la validación del tamaño del búfer quede anulada

Introducción de una nueva función de copia de cadenas

  • Para evitar estos riesgos, se introdujo una función alternativa llamada curlx_strcopy()
    • Recibe como argumentos el búfer de destino, el tamaño del búfer, el búfer de origen y la longitud de la cadena de origen
    • Solo realiza la copia con memcpy() si tanto la copia como la terminación nula son posibles
    • Si falla, inicializa el búfer de destino como una cadena vacía
  • Esta función requiere más argumentos y más código que strcpy(), pero garantiza la seguridad al acoplar estrechamente la validación del búfer con la operación de copia
  • El uso de strcpy() queda completamente prohibido en la base de código de cURL y se elimina igual que strncpy()

Detalles de implementación

  • Un ejemplo de la definición de la función es el siguiente
    void curlx_strcopy(char *dest, size_t dsize, const char *src, size_t slen)
    {
      DEBUGASSERT(slen < dsize);
      if(slen < dsize) {
        memcpy(dest, src, slen);
        dest[slen] = 0;
      }
      else if(dsize)
        dest[0] = 0;
    }
    
  • Mediante DEBUGASSERT, se detectan errores de forma temprana durante el desarrollo, y está diseñada para que en entornos de despliegue real siempre tenga éxito
  • No tiene valor de retorno como strcpy, y se adopta un enfoque de detectar errores en las fases de pruebas y fuzzing

Reacción de la comunidad

  • Algunos desarrolladores señalaron que se parece a strcpy_s() (C11 Annex K), aunque cURL sigue usando el estándar C89
  • Otras opiniones propusieron agregar un valor de retorno o mejorar la forma de manejar fallos del búfer
  • Ante esto, cURL explicó que “como fue diseñada para ser una función que siempre tiene éxito, un valor de retorno es innecesario”

Efecto adicional relacionado con la IA

  • Este cambio también puede evitar el problema de que chatbots de IA detecten erróneamente el uso de strcpy en el código de cURL y afirmen que es vulnerable
  • Aun así, el autor mencionó que “la IA todavía podría inventar otros reportes falsos”, señalando las limitaciones del análisis de código basado en IA

5 comentarios

 
ahwjdekf 2026-01-02

Lo correcto es usar snprintf en lugar de strcpy. Si hay strcpy en el código, hay que averiguar dónde vive el desarrollador que lo hizo.

 
winmain 2026-01-02

Así era como trabajaba con código de depuración cuando estaba en una empresa de videojuegos hace 25 años, y no era solo strcpy. En la versión de lanzamiento se volvía a desactivar para el servicio por la mejora de velocidad. De hecho, en el área de juegos los choques de memoria son de lo más sensible, así que trabajábamos con muchísimo cuidado y atención, e incluso creamos y usamos nuestro propio depurador de memoria. Pero al verlo hoy en día, resulta que eso estaba haciendo recolección de basura. Qué recuerdos tan nostálgicos.

 
secwind 2026-01-02

Error C4996: "strcpy": esta función o variable puede no ser segura. Considere usar strcpy_s en su lugar. Para deshabilitar la advertencia de obsolescencia, use _CRT_SECURE_NO_WARNINGS. Consulte la ayuda en línea para más detalles.

 
GN⁺ 2026-01-01
Comentarios en Hacker News
  • strcpy() no solo es malo desde el punto de vista de la seguridad, sino también en rendimiento
    Antes se pensaba que strcpy() era eficiente cuando no se conocía la longitud de la cadena, pero en realidad, como copia de a un byte por vez, la CPU tiene que hacer predicción de saltos, y eso es ineficiente

    • Ahora pienso que deberíamos abandonar, en la medida de lo posible, las cadenas terminadas en null en sí mismas
    • Últimamente no he visto que strcpy use un bucle escalar. Me pregunto si eso solo ocurre en arquitecturas ARM
  • Todas las rutinas de cadenas de C tienen grandes limitaciones, así que siempre me parecieron poco útiles
    Por eso creo que hace falta una biblioteca que registre, junto con el puntero de cadena, el tamaño de la memoria asignada
    Como ejemplo, vale la pena ver la biblioteca bstring

    • strncpy apareció para copiar nombres de archivo de longitud fija. Para más detalles, ver esta respuesta de StackOverflow
    • No incluir información de longitud en las cadenas se hacía en el pasado para ahorrar memoria. En esa época, incluso un byte era valioso
    • Las funciones de cadenas de C causaron problemas porque sus diseñadores las agregaron sin prever del todo sus consecuencias. Que los arreglos se conviertan forzosamente en punteros al pasarlos como argumentos de función también fue un error de diseño fundamental
    • Este book-keeping adicional antes era una carga, pero hoy ya es algo perfectamente manejable
    • strncpy originalmente era una función para manejar campos de cadena de ancho fijo. Por ejemplo, servía para rellenar con NUL un campo como char username[20]. Ver la documentación relacionada en el manual string_copying.7
  • Me resulta extraño que curlx_strcopy no devuelva si tuvo éxito o no
    Se podría revisar dest[0], pero eso tiene alta probabilidad de inducir errores y no es intuitivo

    • La versión anterior devolvía un error, pero ahora falla en silencio y deja una cadena vacía. Eso es raro
    • Tal vez se asume que, si DEBUGASSERT(slen < dsize); pasa, entonces fue exitoso, pero en un build release pueden eliminarse los assert. Creo que sería mejor un código de error explícito
    • Con un diseño así, veo bastante probable que aparezca un CVE en el futuro
  • strncpy() originalmente no era para cadenas terminadas en null, sino para campos de longitud fija
    El problema empezó cuando los analizadores estáticos recomendaron usar strncpy en lugar de strcpy. Las alternativas reales eran snprintf o strlcpy

    • strlcpy es una función de la familia BSD, así que no está en POSIX. La recomendación oficial es stpecpy, pero casi no hay implementaciones reales. Ver documentación relacionada
    • La razón por la que strncpy rellena después del null era permitir comparaciones eficientes en campos de nombres de longitud fija, como las entradas de directorio. Eso también está especificado en los documentos de fundamento del estándar ANSI C
  • Esta API se siente como Annex-K: el tamaño del buffer de destino incluye el espacio para NUL, pero el tamaño de origen no lo incluye
    Francamente, me parece mejor usar memcpy directamente

  • Me llamó la atención la frase del artículo: “strcpy es un señuelo para que la IA genere reportes de vulnerabilidades incorrectos

    • En la práctica, la IA no solo marca strcpy como problema, sino que genera demostraciones complejas con errores lógicos, y los mantenedores terminan sufriendo para verificarlas
    • Quienes envían estos reportes incorrectos no saben que la IA puede equivocarse, o simplemente no les importa. Después de todo, no hay costo por reportar algo erróneo
    • Al final, el problema es la gente que usa la IA para fines inadecuados
  • El principio de “validar cerca del código” es bueno, pero se vuelve ambiguo cuando hay que validar temprano en el ciclo de vida de los datos
    Creo que sería bueno poder distinguir por tipo, como con Result en Rust, si se trata de “datos validados”

    • Result solo contiene éxito/fracaso, pero no garantiza un estado validado. En cambio, sería mejor tener un tipo separado que solo pueda crearse tras pasar por el proceso de validación. Esa es la filosofía de “parse, don’t validate
    • Idealmente, la validación no debería hacerse cerca del código consumidor, sino lo antes posible en el límite del sistema. Pero para eso hace falta un sistema de tipos expresivo
    • En estos casos, otra opción es distinguir tipos como en String y CharSequence de Java
  • La diferencia off-by-one entre el tamaño del buffer y la longitud de la cadena es un problema de usabilidad terrible. Es muy probable que siga causando errores

  • La nueva función de copia de cadenas propuesta vacía el buffer de destino y devuelve void si no se puede copiar
    Pero en estos casos me parece mejor tratarlo como error y no tocar el buffer. Confiar solo en DEBUGASSERT da poca seguridad

  • Felicidades por completar el proyecto. Incluso en C/C++ se puede lograr seguridad de memoria si se pone esfuerzo
    Pero en móviles el tamaño de letra de las gráficas es demasiado pequeño y dificulta la lectura

    • Quitar strcpy no vuelve al código seguro en memoria automáticamente
    • La tipografía de las gráficas parece pensada para impresión. Para un blog, es demasiado pequeña
 
hiongun 2026-01-04

También está bien pasarse por completo a C3. Como es un proyecto que mantiene la sintaxis de C con cambios mínimos y añade funciones modernas, la migración también es sencilla.