Em caso de inteiro transborda o que é o resultado de (unsigned int) * (int)? sem sinal ou int?

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

  •  05-09-2019
  •  | 
  •  

Pergunta

Em caso de inteiro transborda o que é o resultado de (unsigned int) * (int)? unsigned ou int? Que tipo faz o operador de índice de matriz (operator[]) tomar para char*:? int, unsigned int ou qualquer outra coisa

Eu estava auditar a seguinte função, e de repente esta questão surgiu. A função tem uma vulnerabilidade na linha 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 tanto w e h são muito grandes inteiros sem sinal. A multiplicação na linha 9 tem a chance de passar a validação.

Agora o problema é na linha 17. int i Multiply com unsigned int w: se o resultado for int, é possível que o produto é negativo, resultando no acesso a uma posição que é antes buf. Se o resultado for unsigned int, o produto será sempre positivo, resultando no acesso a uma posição que é depois buf.

É difícil escrever código para justificar esta: int é muito grande. Alguém tem ideias sobre isso?

Existe alguma documentação que especifica o tipo de produto? Eu tenho procurado, mas até agora não encontraram nada.

Suponho que, tanto quanto a vulnerabilidade está em causa, se (unsigned int) * (int) produz unsigned int ou int não importa, porque no arquivo objeto compilado, eles são apenas bytes. O código a seguir funciona o mesmo, não importa o tipo do produto:

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

Portanto, não importa qual o tipo a multiplicação retornos. É importante que se a função consumidor toma int ou unsigned.

A questão aqui é não o quão ruim a função é, ou como melhorar a função para torná-lo melhor. A função sem dúvida, tem uma vulnerabilidade. A pergunta é sobre o comportamento exato da função, com base no comportamento prescrito dos padrões.

Foi útil?

Solução

Para responder à sua pergunta:. O tipo de uma expressão multiplicando um int e um int não assinado será um int não assinado em C / C ++

Para responder à sua pergunta implícita, de uma forma decente para lidar com possível estouro na aritmética inteira é usar o conjunto "IntSafe" de rotinas da Microsoft:

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

Está disponível no SDK e contém implementações em linha para que você possa estudar o que eles estão fazendo, se você estiver em outra plataforma.

Outras dicas

fazer o w h cálculo * no longa, longa, verifique se maior do que MAX_UINT

EDIT: alternativa: se sobrevoados (w * h) / h = w (?!? É este sempre é o caso deve ser, à direita)

Certifique-se que w * h não transborde, limitando w e h.

O tipo de w*i não está assinado no seu caso. Se eu ler o padrão corretamente, a regra é que os operandos são convertidos para o tipo maior (com a sua signedness), ou sem assinatura tipo correspondente ao tipo assinado (que é unsigned int no seu caso).

No entanto, mesmo que seja assinado, ele não impede a envolvente (escrita à memória antes buf), porque ele poderia ser o caso (em i386 plataforma, que é), que p[-1] é o mesmo que p[-1u]. De qualquer forma, no seu caso, tanto buf[-1] e buf[big unsigned number] seria um comportamento indefinido, de modo que o assinaram pergunta / sem assinatura não é tão importante.

Note que assinou / matérias não assinados em outros contextos - por exemplo. (int)(x*y/2) dá resultados diferentes dependendo dos tipos de x e y, mesmo na ausência de um comportamento indefinido.

Gostaria de resolver o seu problema através da verificação de estouro na linha 9; desde 4096 é uma muito pequena constante e 4096 * 4096 não transborde na maioria das arquiteturas (você precisa verificar), eu faria

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

Esta deixa de fora o caso quando w ou h são 0, você pode querer verificar para ele, se necessário.

Em geral, você pode verificar para estouro assim:

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

Em C / C ++ a notação p[n] é realmente um atalho para a escrita *(p+n), e este aritmética de ponteiro leva em consideração o sinal. Então p[-1] é válido e se refere ao valor imediatamente antes *p.

Assim, o sinal realmente importa aqui, o resultado do operador de aritmética com inteiros seguir um conjunto de regras definidas pela norma, e isso é chamado inteiro promoções.

Visitar esta página: INT02-C. Entenda inteiro regras de conversão

2 alterações torná-lo mais seguro:

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

...

unsigned i;

Note também que não menos é uma má idéia para escrever ou ler além do fim do buffer. Portanto, a questão não é se i w pode tornar-se negativo, mas se 0 <= i h + w <= 4096 mantém-se.

Portanto, não é o tipo que importa, mas o resultado de h * i. Por exemplo, não faz diferença se isso é (não assinada) 0x80000000 ou (int) 0x80000000, o programa irá seg-fault qualquer maneira.

Para C, consulte "conversões usuais aritméticas". (C99: Secção 6.3.1.8, ANSI C K & R A6.5) para detalhes sobre como os operandos dos operadores matemáticos são tratados

No seu exemplo as seguintes regras aplicam-se:

C99:

Caso contrário, se o tipo do operando com assinado tipo inteiro pode representar todos os valores do tipo do operando com tipo inteiro sem sinal, em seguida, o operando com inteiro sem sinal tipo é convertido para o tipo de operando com inteiro assinado tipo.

Caso contrário, ambos os operandos são convertidos para o tipo inteiro sem sinal correspondente ao tipo da operando com inteiro assinado tipo.

ANSI C:

Caso contrário, se qualquer operando for unsigned int, o outro é convertido para int não assinado.

Porque não basta declarar i como int não assinado? Então, o problema desaparece.

Em qualquer caso, i * w é garantido para ser <= 4096, como os testes de código para isso, por isso nunca vai transbordar.

memcpy (& buf [i w> -1 i w <4097 i W: 0: 0], o init, w); Eu não acho que o cálculo triplo da i w faz degradar a perfomance)

w * h pode transbordar se w e / ou h são suficientemente grande e o seguinte validação poderia passar.

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

No int, operações mistas unsigned int, int é elevado a int não assinado, caso em que, um valor negativo de 'i' se tornaria um grande valor positivo. Nesse caso

&buf[i*w]

seria acesso a um fora de valor vinculado.

aritmética sem sinal é feito como modular (ou wrap-around), de modo que o produto de dois grandes ints não assinados podem facilmente ser inferior a 4096. A multiplicação de int e int não assinado resultará em um int não assinado (ver secção 4.5 do C ++ padrão).

Por isso, dado grande w e um valor adequado de h, você pode realmente entrar em apuros.

Certificar-se de aritmética inteira não estouro é difícil. Uma maneira fácil é converter a ponto flutuante e fazendo uma multiplicação de ponto flutuante, e ver se o resultado é de todo razoável. Como QWERTY sugeriu, longa, longa seria útil, se disponível no seu implementação. (É uma extensão comum em C90 e C ++, existe em C99, e estará em C ++ 0x).

Existem 3 parágrafos no actual projecto de C1X no cálculo (UNSIGNED TIPO1) X (TIPO2 ASSINADO) em 6.3.1.8 coversions aritméticas usuais, N1494,

WG 14: C - A situação do projeto e marcos

Caso contrário, se o operando que tem tipo inteiro não assinado tem posto maior ou igual à classificação do tipo do outro operando, então o operando com Tipo inteiro assinado é convertido para o tipo de operando com unsigned tipo inteiro.

Caso contrário, se o tipo do operando com o tipo inteiro assinado pode representar todos os valores do tipo do operando com o tipo inteiro sem sinal, então operando com tipo inteiro sem sinal é convertido para o tipo de operando com inteiro assinado tipo.

De outro modo, ambos os operandos são convertidos para o tipo inteiro sem sinal correspondente ao tipo do operando com o tipo inteiro assinado.

Portanto, se um não está assinado int e b é int, análise de (a * b) deve gerar o código (a * (unsigned int) b). Transbordará se b <0 ou a * b> UINT_MAX.

Se um não está assinado int e b é longa de tamanho maior, (a * b) deve gerar ((long) a * (longo) b). Transbordará se a * b> LONG_MAX ou a * b

Se um não está assinado int e b é longo do mesmo tamanho, (a * b) deve gerar ((unsigned long) a * (unsigned long) b). Transbordará se b <0 ou a * b> ULONG_MAX.

Em sua segunda pergunta sobre o tipo esperado pelo "indexador", a resposta aparece "tipo inteiro", que permite que qualquer (assinado) índice inteiro.

6.5.2.1 Matriz subscripting

Restrições

1 Uma das expressões terão tipo ‘‘ponteiro para tipo de objeto completo’’, o outro expressão deve ter tipo integer, eo resultado tem tipo ‘‘tipo’’.

Semântica

2 A expressão sufixo seguido por uma expressão entre parêntesis rectos [] é um subscripted designação de um elemento de um objecto matriz. A definição do operador subscrito [] é que E1 [E2] é idêntico a (* ((E1) + (E2))). Por causa das regras de conversão que aplicar ao operador + binário, se E1 é um objeto de matriz (de modo equivalente, um ponteiro para o elemento inicial de um objecto matriz) e E2 é um número inteiro, E1 [E2] designa a E2-th elemento de E1 (contagem de zero).

Cabe para o compilador para executar uma análise estática e avisar o desenvolvedor sobre possibilidade de transbordo na memória intermédia, quando a expressão de ponteiro é uma variável de matriz e o índice pode ser negativo. O mesmo vale cerca de advertência sobre derrapagens tamanho possível da matriz, mesmo quando o índice é positivo ou não assinado.

Para realmente responder sua pergunta, sem especificar o hardware que está sendo executado, você não sabe, e em código pretende ser portátil, você não deve depender de qualquer comportamento particular.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top