@koraaのクレジット。
現在、このコードは無料発行後に使用されます。
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);
}
これは、 a->secret
がa->customSecret
a->secret
指しているためであり、 XXH3_copyState
はそのポインタを更新しないため(文字通りmemcpy
)、 b->secret
はa->customSecret
b->secret
指します。
a
が解放された後、 b->secret
は無効なポインタになります。
#361の説明を参照してください。
いくつかのオプションがあります。
動作に変更がない最初の最も単純なオプションは次のとおりです。
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;
}
しかし、これは少しハッキーです。
2番目のオプションは、予約済みフィールドの1つを使用して、それがどの種類の状態であるかを示し、生のポインターを比較する代わりにそれを使用することです。
これを実行しているときに、エラーチェックを追加して、64ビットと128ビットの状態が混在しないようにすることができます。 これにより動作が変わりますが、その使用法は意図されたものではなく、とにかくUBと見なすことができます。
3番目のオプションは、シードモードにNULL
ポインターを使用することです。これがNULLの場合、 customSecret
代わりにsecret
customSecret
使用しますが、これによりブランチが追加されます。 ただし、状態管理からのオーバーヘッドはすでに十分にあるため、目立たない違いが生じる可能性があります。
最後のオプションは、シークレットポインタのコピーをmalloc()
することです。
これにより、ABIが変更されます。これは、スタックメモリ上で完全に実行することができなくなったためです。 また、 kSecret
場合は非常に冗長になります。
ただし、 reset_withSeed()
後でカスタムシークレットを無効にすることができ、状態のサイズを縮小することができます。
おっと、誤って間違ったボタンを押しました:facepalm:
4番目のオプションがあると思います。常にエントロピープールを構造にコピーします。 ただし、これにより、プールはデフォルトのシークレットのサイズになります。 それはちょっといいかもしれませんが、オフラインエントロピープール生成の最適化の基礎を形成する可能性があります。
uint64_t XXH3_64bits_withEntropyPool(XXH3_state_t &pool, uint8_t data, size_t size);
実装は、指定されたデータでXXH3状態を「拡張」します。 状態を変更せずにすぐにハッシュを返します…
編集これは可動性を維持します。 どのIMOは使いやすさにとって大きな恩恵であり、錆の統合に高く評価されます。
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.
これは何ですか? あなたがそこで何をしようとしているのかわかりません…:smirk:
可動性は素晴らしい機能であり、固定サイズの秘密で簡単になります。
実際には、固定サイズのシークレットを使用するとすべてが簡単(かつ高速)になりますが、APIを台無しにしたくありません。
最良のオプションはNULL
ポインタだと思います。
XXH3_update
の先頭にはすでに5つのブランチがあります。 もう1つ実行しても問題はありません(特に、バッファーがいっぱいになったときにのみ必要になるため)
x86_64とi686は、分岐せずにこれを実行できることにも注意してください。
lea tmp, [state + offsetof (state::customSecret)]
test secret, secret
cmove secret, tmp
これもARMではブランチレスです
cmp secret, #0
it eq
addeq secret, state, offsetof(state::customSecret)
およびAArch64の場合:
add tmp, state, offsetof(state::customSecret)
cmp secret, #0
csel secret, tmp, secret, eq
最も参考になるコメント
最良のオプションは
NULL
ポインタだと思います。XXH3_update
の先頭にはすでに5つのブランチがあります。 もう1つ実行しても問題はありません(特に、バッファーがいっぱいになったときにのみ必要になるため)