Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

121
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda