Xxhash: Use after free com streaming XXH3 seeded + copyState

Criado em 19 mai. 2020  ·  7Comentários  ·  Fonte: Cyan4973/xxHash

Crédito para @koraa.

Atualmente, este código exibe um uso após edição gratuita:

void bugged(void)
{
    XXH3_state_t *a = XXH3_createState();
    XXH3_state_t *b = XXH3_createState();
    XXH3_64bits_reset_withSeed(a, 0x1234567890abcdefull);
    XXH3_copyState(b, a);
    XXH3_freeState(a);
    // Bug
    XXH3_64bits_update(b, "BUGGED", strlen("BUGGED"));
    XXH3_freeState(b);
}

Isso se deve ao fato de a->secret apontar para a->customSecret , e XXH3_copyState não atualizar esse ponteiro (ele é literalmente apenas memcpy ), então b->secret pontos para a->customSecret .

Após a ser liberado, b->secret é agora um ponteiro inválido.

xxh3 use after free

Comentários muito úteis

Acho que a melhor opção é o ponteiro NULL .

Já existem 5 ramos no início de XXH3_update . Não custa fazer mais um (especialmente porque só é necessário quando o buffer está cheio)

Todos 7 comentários

Veja a discussão em # 361.

Existem algumas opções.

A primeira e mais simples opção que não altera o comportamento é esta:

XXH_PUBLIC_API XXH_errorcode
XXH3_copyState(XXH3_state_t* dst, const XXH3_state_t* src)
{
    XXH_memcpy(dst, src, sizeof(XXH3_state_t));
    if (src->secret == &src->customSecret[0]) {
        dst->secret = &dst->customSecret[0];
    }
    return XXH_OK;
}

Isso é um pouco hackeado.

A segunda opção é usar um dos campos reservados para informar qual tipo de estado ele está e usá-lo em vez de comparar ponteiros brutos.

Enquanto fazemos isso, poderíamos adicionar verificação de erros para evitar a mistura de estados de 64 bits e 128 bits. Isso mudaria o comportamento, mas esse uso não foi intencional e pode ser considerado UB.

A terceira opção é usar um ponteiro NULL para o modo propagado, e se for NULL, use customSecret vez de secret , mas isso adicionaria um branch. No entanto, já existe sobrecarga suficiente do gerenciamento de estado que provavelmente faria uma diferença imperceptível.

A última opção é malloc() uma cópia do ponteiro secreto.

Isso mudará o ABI, pois não é mais possível executá-lo inteiramente na memória da pilha. Também será altamente redundante para kSecret .

No entanto, isso permitirá que os segredos personalizados sejam invalidados após reset_withSeed() e nos permitirá reduzir o tamanho do estado.

Opa, apertou acidentalmente o botão errado: facepalm:

Acho que há uma quarta opção: sempre copie o pool de entropia na estrutura; isso forçaria o pool a ter o tamanho do segredo padrão. Isso pode ser bom e pode formar a base para a otimização de geração de pool de entropia offline.

uint64_t XXH3_64bits_withEntropyPool(XXH3_state_t &pool, uint8_t data, size_t size);

A implementação iria "estender" o estado XXH3 com os dados fornecidos; retornar imediatamente o hash sem modificar o estado ...

EDITAR Isso preservaria a mobilidade; qual IMO é um grande benefício para a usabilidade e seria muito apreciado por uma integração com ferrugem.

uint64_t XXH3_64bits_withEntropyPool(XXH3_state_t &pool, uint8_t data, size_t size);
error: expected ')'
...XXH3_64bits_withEntropyPool(XXH3_state_t &pool, uint8_t data,...
                                            ^
note: to match this '('
uint64_t XXH3_64bits_withEntropyPool(XXH3_state_t &pool, uint8_t...
                                    ^
1 error generated.

O que é isso? Eu não sei o que você está tentando fazer aí ...: sorriso:

A mobilidade seria um bom recurso e seria fácil com um segredo de tamanho fixo.

Na verdade, tudo seria mais fácil (e mais rápido) com um segredo de tamanho fixo, mas não queremos bagunçar a API.

Acho que a melhor opção é o ponteiro NULL .

Já existem 5 ramos no início de XXH3_update . Não custa fazer mais um (especialmente porque só é necessário quando o buffer está cheio)

Também deve ser observado que x86_64 e i686 podem fazer isso sem ramificação:

        lea     tmp, [state + offsetof (state::customSecret)]
        test    secret, secret
        cmove   secret, tmp

Isso também não tem ramificações no ARM

        cmp     secret, #0
        it      eq
        addeq   secret, state, offsetof(state::customSecret)

e em AArch64:

        add     tmp, state, offsetof(state::customSecret)
        cmp     secret, #0
        csel    secret, tmp, secret, eq
Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

eloff picture eloff  ·  6Comentários

devnoname120 picture devnoname120  ·  8Comentários

t-mat picture t-mat  ·  3Comentários

hiqbn picture hiqbn  ·  7Comentários

ifduyue picture ifduyue  ·  8Comentários