En caso de desbordamiento de enteros, ¿cuál es el resultado de (unsigned int) * (int)?sin firmar o int?

StackOverflow https://stackoverflow.com/questions/721861

  •  05-09-2019
  •  | 
  •  

Pregunta

En caso de desbordamiento de números enteros, ¿cuál es el resultado de (unsigned int) * (int) ? unsigned o int?¿Qué tipo tiene el operador de índice de matriz (operator[]) tomar por char*: int, unsigned int ¿o algo mas?

Estaba auditando la siguiente función y de repente surgió esta pregunta.La función tiene una vulnerabilidad en la línea 17.

// Create a character array and initialize it with init[] 
// repeatedly. The size of this character array is specified by 
// w*h.
char *function4(unsigned int w, unsigned int h, char *init)
{
    char *buf;
    int i;

    if (w*h > 4096)
        return (NULL);

    buf = (char *)malloc(4096+1);
    if (!buf)
        return (NULL);

    for (i=0; i<h; i++)
        memcpy(&buf[i*w], init, w);  // line 17

    buf[4096] = '\0';

    return buf;
}

Considere ambos w y h son enteros muy grandes sin signo.La multiplicación en la línea 9 tiene la posibilidad de pasar la validación.

Ahora el problema está en la línea 17.Multiplicar int i con unsigned int w:si el resultado es int, es posible que el producto sea negativo, resultando en acceder a una posición anterior buf.Si el resultado es unsigned int, el producto siempre será positivo, resultando en acceder a una posición que está después buf.

Es difícil escribir código para justificar esto: int Es demasiado largo.¿Alguien tiene ideas sobre esto?

¿Existe alguna documentación que especifique el tipo de producto?Lo he buscado, pero hasta ahora no he encontrado nada.

Supongo que en lo que respecta a la vulnerabilidad, ya sea (unsigned int) * (int) produce unsigned int o int No importa, porque en el archivo objeto compilado, son solo bytes.El siguiente código funciona igual sin importar el tipo de producto:

unsigned int x = 10;
int y = -10;

printf("%d\n", x * y);  // print x * y in signed integer
printf("%u\n", x * y);  // print x * y in unsigned integer

Por lo tanto, no importa de qué tipo resulte la multiplicación.Importa si la función del consumidor toma int o unsigned.

La pregunta aquí es no qué tan mala es la función o cómo mejorarla para mejorarla.La función sin duda tiene una vulnerabilidad.La pregunta es sobre el comportamiento exacto de la función, según el comportamiento prescrito por los estándares.

¿Fue útil?

Solución

Para responder a su pregunta:. El tipo de una expresión multiplicar un entero y un entero sin signo será un entero sin signo en C / C ++

Para responder a su pregunta implícita, de una manera digna de tratar la posible desbordamiento en aritmética de enteros es usar el "IntSafe" conjunto de rutinas de Microsoft:

http://blogs.msdn.com/michael_howard /archive/2006/02/02/523392.aspx

Está disponible en el SDK y contiene implementaciones en línea para que pueda estudiar lo que están haciendo, si estás en otra plataforma.

Otros consejos

hacer el cálculo w * h en mucho tiempo, comprobar si más grande que MAX_UINT

EDIT: alternativa: si sobrevolado (w * h) / h = w (?!? Es este siempre el caso debería ser, a la derecha)

Asegúrese de que w * h no rebose, mediante la limitación de w y h.

El tipo de w*i no está firmado en su caso. Si leo la norma correctamente, la regla es que los operandos se convierten al tipo más grande (con su signo numérico), o de tipo sin signo correspondiente al tipo firmado (que se unsigned int en su caso).

Sin embargo, aunque sea sin firmar, que no impide que la envolvente (escrito a la memoria antes de buf), ya que podría ser el caso (en la plataforma i386, lo es), que p[-1] es la misma que p[-1u]. De todos modos, en su caso, tanto buf[-1] y buf[big unsigned number] serían indefinidos comportamiento, así que la pregunta firmado / sin signo no es tan importante.

Tenga en cuenta que firmó / asuntos sin firmar en otros contextos - por ejemplo. (int)(x*y/2) da resultados diferentes dependiendo de los tipos de x y y, incluso en ausencia de un comportamiento indefinido.

Me resolver su problema mediante la comprobación de desbordamiento en la línea 9; desde 4096 es una muy pequeña constante y 4096 * 4096 no se desborde en la mayoría de arquitecturas (es necesario comprobar), lo haría

if (w>4096 || h>4096 || w*h > 4096)
     return (NULL);

Esto deja fuera el caso cuando w o h son 0, es posible que desee comprobar si es necesario.

En general, se puede comprobar si hay desbordamiento de la siguiente manera:

if(w*h > 4096 || (w*h)/w!=h || (w*h)%w!=0)

En C/C++ el p[n] La notación es realmente un atajo para escribir. *(p+n), y esta aritmética de punteros tiene en cuenta el signo.Entonces p[-1] es válido y se refiere al valor inmediatamente anterior *p.

Entonces, el signo realmente importa aquí, el resultado del operador aritmético con un número entero sigue un conjunto de reglas definidas por el estándar, y esto se llama promociones de números enteros.

Mira esta página: INT02-C.Comprender las reglas de conversión de números enteros

2 cambios hacen que sea más seguro:

if (w >= 4096 || h >= 4096 || w*h > 4096)  return NULL;

...

unsigned i;

Tenga en cuenta también que no menos es una mala idea para escribir o leer más allá del final del búfer. Así que la pregunta no es si i w puede llegar a ser negativo, pero si 0 <= i h + w <= 4096 se mantiene.

Así que no es del tipo que importa, sino el resultado de h i *. Por ejemplo, no hace diferencia si esto es culpa SEG-0x80000000 o (int) 0x80000000, el programa (sin firmar) de todos modos.

En C, se refieren a "conversiones habituales aritméticas". (C99: Sección 6.3.1.8, ANSI C K & R A6.5) para obtener detalles sobre cómo son tratados los operandos de los operadores matemáticos

En su ejemplo se aplican las siguientes reglas:

C99:

  

De lo contrario, si el tipo del operando   con firmado tipo entero puede representar   todos los valores del tipo de la   operando con el tipo de número entero sin signo,   a continuación, el operando con entero sin signo   tipo se convierte en el tipo de la   operando con el tipo entero con signo.

     

Si no, se convierten los dos operandos   al tipo entero sin signo   correspondiente al tipo de la   operando con el tipo entero con signo.

ANSI C:

  

lo contrario, si alguno de los operandos es unsigned int, el otro es convertido a unsigned int.

¿Por qué no declarar int i como sin firmar? Entonces, el problema desaparece.

En cualquier caso, i * w se garantiza que sea <= 4096, ya que las pruebas de código para esto, por lo que nunca va a desbordarse.

memcpy (y buf [i w> -1 i w <4,097 i W: 0:? 0], init, w); No creo que el cálculo de la Triple i w hace degradar la perfomance de)

w * h podría rebosadero si w y / o h son suficientemente grandes y la siguiente validación podría pasar.

9.      if (w*h > 4096)
10.         return (NULL);

En int, operaciones mixtas unsigned int, int es elevado a unsigned int, en cuyo caso, un valor negativo de 'i' se convertiría en un valor positivo grande. En ese caso

&buf[i*w]

se accede a un valor de cota.

aritmética sin signo se hace como modular (o envolventes), por lo que el producto de dos grandes enteros sin signo puede fácilmente ser inferior a 4096. La multiplicación de int y unsigned int resultará en un unsigned int (véase la sección 4.5 de la C ++ estándar).

Por lo tanto, dado gran w y un valor adecuado de horas, puede de hecho tener problemas.

Asegurarse de que la aritmética de enteros no es difícil de desbordamiento. Una forma fácil es convertir a punto flotante y hacer una multiplicación de punto flotante, y ver si el resultado es en absoluto razonable. Como sugirió qwerty, largo, largo sería útil, si está disponible en su aplicación. (Es una extensión común en C90 y C ++, existe en C99, y estará en C ++ 0x.)

Hay 3 párrafos del proyecto C1X actual en el cálculo de (UNSIGNED TIPO 1) X (FIRMADO TYPE2) en 6.3.1.8 coversions aritméticas habituales, N1494,

WG 14: C - Estado del proyecto y los hitos

  

De lo contrario, si el operando que tiene tipo entero sin signo tiene rango mayor o   igual al rango del tipo del otro operando, a continuación, el operando con   firmado tipo entero se convierte en el tipo del operando con unsigned   tipo entero.

     

De lo contrario, si el tipo del operando con el tipo entero con signo puede representar   todos los valores del tipo del operando con el tipo de número entero sin signo, a continuación,   el operando con el tipo entero sin signo se convierte en el tipo de la   operando con el tipo entero con signo.

     

De lo contrario, los dos operandos se convierten al tipo entero sin signo   correspondiente al tipo del operando con el tipo entero con signo.

Así que si a es unsigned int yb es int, el análisis sintáctico de (a * b) debe generar el código (a * (unsigned int) b). Se desbordará si b <0 ó a * b> UINT_MAX.

Si a es unsigned int y b es larga de mayor tamaño, (a * b) debe generar ((largo) a * (largo) b). Se desbordará si a * b> LONG_MAX o a * b

Si a es unsigned int y b es largo del mismo tamaño, (a * b) debe generar ((unsigned long) a * (unsigned long) b). Se desbordará si b <0 ó a * b> ULONG_MAX.

Sobre la segunda pregunta sobre el tipo esperado por "paso a paso", la respuesta parece "tipo entero", que permite que cualquier (firmado) índice entero.

  

6.5.2.1 subíndices de matriz

     

Restricciones

     

1 Una de las expresiones tendrán el tipo ‘‘puntero a completar tipo de objeto’’, el otro   expresión tendrá tipo entero, y el resultado ha escribir ‘‘tipo’’.

     

Semántica

     

2 Una expresión postfix seguido de una expresión en corchetes [] es un subíndice   designación de un elemento de un objeto de matriz. La definición del operador subíndice []   es que E1 [E2] es idéntica a (* ((E1) + (E2))). Debido a las reglas de conversión que   aplicar al operador binario +, si E1 es un objeto array (equivalentemente, un puntero a la   elemento inicial de un objeto de matriz) y E2 es un número entero, E1 [E2] designa el E2-ésimo   elemento de E1 (a contar desde cero).

Es hasta el compilador para realizar el análisis estático y advertir al desarrollador sobre la posibilidad de saturación del búfer cuando la expresión puntero es una variable de matriz y el índice puede ser negativo. Lo mismo va en la advertencia sobre los excesos de tamaño posible de la matriz, incluso cuando el índice es positivo o sin signo.

Para responder a su pregunta en realidad, sin especificar el hardware se está ejecutando en, usted no sabe, y en el código destinado a ser portátil, usted no debe depender de ningún comportamiento en particular.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top