Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

120
Vistas
gcc-11 incorrecto puede usarse sin inicializar que parece difícil de evitar

Encontré un patrón de uso particular que parece completamente correcto y ningún compilador se ha quejado antes. Ahora genera una advertencia con gcc-11: a continuación se muestra un ejemplo casi mínimo. Guarde como tc y compile usando gcc-11 -O2 -Wall -c tc .

 #include <stdlib.h> #include <string.h> extern void f(const char *s); void t(int len, char *d) { char tmp[500]; char *s, *o; int i; if ( len <= sizeof(tmp) ) s = tmp; else if ( !(s=malloc(len)) ) return; for(o=s,i=0; i < len; i++) *o++ = d[i]+1; f(s); // i = strlen(s); if ( s != tmp ) free(s); }

Compilar resultados en:

 gcc-11 -O2 -Wall -c tc tc: In function 't': tc:20:3: warning: 's' may be used uninitialized [-Wmaybe-uninitialized] 20 | f(s); | ^~~~ tc:4:13: note: by argument 1 of type 'const char *' to 'f' declared here 4 | extern void f(const char *s); | ^ tc:20:3: warning: 'tmp' may be used uninitialized [-Wmaybe-uninitialized] 20 | f(s); | ^~~~ tc:4:13: note: by argument 1 of type 'const char *' to 'f' declared here 4 | extern void f(const char *s); | ^ tc:8:8: note: 'tmp' declared here 8 | { char tmp[500]; | ^~~

Ahora hay algunas observaciones.

  • llamar a strlen(s) en lugar de f(s) no genera una advertencia. Tenga en cuenta que ambos aceptan const char* (marcado en /usr/include/string.h )
  • Si elimino la const en la declaración de f() , el problema también desaparece.
  • Llamar usando f((const char*)s) tampoco ayuda.
  • Inicializar s como en char *s = NULL tampoco ayuda (y no quiero eso de todos modos, ya que oculta las advertencias correctas sobre no inicializar una variable).

Aprendí que afirmar que algo es un error de GCC generalmente se demuestra con razón que es incorrecto. Así que primero verifico aquí lo que me estoy perdiendo.

editar Como no puedo poner código en el comentario, aquí hay un código que refuta parte de la afirmación. Esto compila bien:

 extern void f(const char *__s); void t(int len, char *d) { char tmp[500]; char *s=tmp, *o; int i; for(o=s,i=0; i < len; i++) *o++ = d[i]+1; f(tmp); }
over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

El compilador es correcto, aunque la redacción en inglés es imperfecta.

Suponiendo que len siempre sea positivo, una solución es insertar if (len <= 0) __builtin_unreachable(); en la función. Esto le dice al compilador que len siempre es positivo, lo que significa que algunos datos deben escribirse en s puntos de memoria antes de llamar a f .

Cuando el compilador dice que "'s' se puede usar sin inicializar", no significa que se puede usar el valor de s , sino que se puede usar lo que apunta, y que la memoria apuntada no se inicializa. Tenga en cuenta que s se pasa a una función que toma un const char *s , lo que sugiere que la función no modificará los datos a los que apunta s y, por lo tanto, espera que ya contenga datos. El estándar C no requiere estrictamente esto; siempre que el apuntado a la memoria no se haya definido con const , f puede reconvertir el puntero a su char * original y modificar los datos allí, pero la implicación de la declaración del parámetro es que no lo hará.

Podemos confirmar esto cambiando el cuerpo de la función a:

 char tmp[500]; f(tmp);

Luego, el compilador se queja de "advertencia: 'tmp' puede usarse sin inicializar". Es patentemente obvio que tmp , como se pasa a la función, no está sin inicializar; será la dirección de la matriz. Por lo tanto, el compilador debe advertir que es el contenido de la matriz el que se puede usar sin inicializar.

Tenga en cuenta que, mientras que el ciclo que comienza con for(o=s,i=0; i < len; i++) aparentemente inicializa los datos a los que apunta s , no lo hace si len es cero. Y dado que s se pasa a f sin len , f no tiene forma de saber que no hay nada en s (excepto por algún canal lateral, como el uso de objetos externos). Entonces, presumiblemente, f lee al menos algunos datos en s en cada llamada.

Aquí hay un ejemplo más pequeño:

 #include <stdlib.h> extern void f(const char *s); void t(int len) { char *s = malloc(len); f(s); free(s); }

Presumiblemente, len siempre es positivo. Para decirle a GCC esto, inserte esta línea en la función:

 if (len <= 0) __builtin_unreachable();

Eso da como resultado que no se genere un nuevo código, pero la advertencia desaparece. (En realidad, el código generado se vuelve más pequeño, en parte porque el compilador puede ingresar al bucle for sin probar primero i < len ).

over 4 years ago · Santiago Trujillo Denunciar

0

Se explicó en las notas de la versión de gcc-11

https://www.gnu.org/software/gcc/gcc-11/changes.html

quizás-no inicializado ahora está incluido por defecto

https://gcc.gnu.org/onlinedocs/gcc-11.1.0/gcc/Warning-Options.html#index-Wmaybe-uninitialized

El compilador emite una advertencia si no puede probar que las rutas no inicializadas no se ejecutan en tiempo de ejecución.

Incluso si está claro para el autor, el compilador tiene la responsabilidad de probar que los datos se inicializaron; de lo contrario, establece una advertencia.

-Wmaybe-uninitialized se activa con -Wall

La verdadera pregunta es: ¿por qué no simplemente inicializar los datos?

Este es un código de ejemplo, pero parece una vulnerabilidad en espera de una explotación dado que tmp puede ser cualquier memoria no nula arbitraria, presumiblemente len está ahí porque no hay garantía de un terminador nulo (o nulos incrustados pueden estar presentes), pero len no está 't pasó a través de f .

 #include <stdlib.h> #include <string.h> extern void f(const char *s); void t(int len, char *d) { char tmp[500] = {0}; //ensure nulls char *s = tmp; char *o = s; int i; if ( len <= sizeof(tmp) ) s = tmp; else if ( !(s=malloc(len)) ) return; memset(s+len, 0, 1); // ensure a trailing null for(i=0; i < len-1; i++) // leave that null there at least *o++ = d[i]+1; f(s); // i = strlen(s); if ( s != tmp ) free(s); }

gcc lo compila sin advertencias

 gcc -O2 -Wall -c tc
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda