diff mbox series

[ovs-dev,v3,3/3] hash: Avoid 64bit crc intrinsics on 32bit aligned data

Message ID 20230130192341.514178-3-mkp@redhat.com
State Superseded
Headers show
Series [ovs-dev,v3,1/3] netdev-dummy: Allocate dummy_packet_stream on cacheline boundary | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/intel-ovs-compilation fail test: fail

Commit Message

Mike Pattrick Jan. 30, 2023, 7:23 p.m. UTC
UB Sanitizer report:

lib/hash.h:219:17: runtime error: load of misaligned address
0x7ffc164a88b4 for type 'const uint64_t', which requires 8 byte
alignment

    #0 in hash_words_inline lib/hash.h:219
    #1 in hash_words lib/hash.h:297
    [...]

Signed-off-by: Mike Pattrick <mkp@redhat.com>
---
v2:
- Fixed compile error on non-SSE4 CPUs
- Changed [] to * in function parameters
- Tested on aarch64 as well
v3:
- No change
---
 lib/hash.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/lib/hash.h b/lib/hash.h
index 60a39a40b..7b7f70c11 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -187,13 +187,72 @@  static inline uint32_t hash_finish(uint64_t hash, uint64_t final)
     return hash ^ (uint32_t)hash >> 16; /* Increase entropy in LSBs. */
 }
 
+static inline uint32_t
+hash_finish32(uint64_t hash, uint32_t final, uint32_t semifinal)
+{
+    /* The finishing multiplier 0x805204f3 has been experimentally
+     * derived to pass the testsuite hash tests. */
+    hash = _mm_crc32_u32(hash, semifinal);
+    hash = _mm_crc32_u32(hash, final) * 0x805204f3;
+    return hash ^ ((uint32_t) hash >> 16); /* Increase entropy in LSBs. */
+}
+
+static inline uint32_t
+hash_words_32aligned(const uint32_t *p_, size_t n_words, uint32_t basis)
+{
+    const uint32_t *p = (const void *) p_;
+    uint32_t hash1 = basis;
+    uint32_t hash2 = 0;
+    uint32_t hash3 = n_words;
+    const uint32_t *endp = (const uint32_t *) p + n_words;
+    const uint32_t *limit = p + n_words - 6;
+
+    while (p <= limit) {
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        hash1 = _mm_crc32_u32(hash1, p[1]);
+        hash2 = _mm_crc32_u32(hash2, p[2]);
+        hash2 = _mm_crc32_u32(hash2, p[3]);
+        hash3 = _mm_crc32_u32(hash3, p[4]);
+        hash3 = _mm_crc32_u32(hash3, p[5]);
+        p += 6;
+    }
+    switch (endp - (const uint32_t *) p) {
+    case 1:
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        break;
+    case 2:
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        hash1 = _mm_crc32_u32(hash1, p[1]);
+        break;
+    case 3:
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        hash1 = _mm_crc32_u32(hash1, p[1]);
+        hash2 = _mm_crc32_u32(hash2, p[2]);
+        break;
+    case 4:
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        hash1 = _mm_crc32_u32(hash1, p[1]);
+        hash2 = _mm_crc32_u32(hash2, p[2]);
+        hash2 = _mm_crc32_u32(hash2, p[3]);
+        break;
+    case 5:
+        hash1 = _mm_crc32_u32(hash1, p[0]);
+        hash1 = _mm_crc32_u32(hash1, p[1]);
+        hash2 = _mm_crc32_u32(hash2, p[2]);
+        hash2 = _mm_crc32_u32(hash2, p[3]);
+        hash3 = _mm_crc32_u32(hash3, p[4]);
+        break;
+    }
+    return hash_finish32(hash1, hash2, hash3);
+}
+
 /* Returns the hash of the 'n' 32-bit words at 'p_', starting from 'basis'.
  * We access 'p_' as a uint64_t pointer, which is fine for __SSE_4_2__.
  *
  * This is inlined for the compiler to have access to the 'n_words', which
  * in many cases is a constant. */
 static inline uint32_t
-hash_words_inline(const uint32_t p_[], size_t n_words, uint32_t basis)
+hash_words_inline(const uint32_t *p_, size_t n_words, uint32_t basis)
 {
     const uint64_t *p = (const void *)p_;
     uint64_t hash1 = basis;
@@ -202,6 +261,10 @@  hash_words_inline(const uint32_t p_[], size_t n_words, uint32_t basis)
     const uint32_t *endp = (const uint32_t *)p + n_words;
     const uint64_t *limit = p + n_words / 2 - 3;
 
+    if (OVS_UNLIKELY(((intptr_t) p & ((sizeof(uint64_t)) - 1)) != 0)) {
+        return hash_words_32aligned(p_, n_words, basis);
+    }
+
     while (p <= limit) {
         hash1 = _mm_crc32_u64(hash1, p[0]);
         hash2 = _mm_crc32_u64(hash2, p[1]);