1 /* $NetBSD: t_hash.c,v 1.2 2025/04/16 02:27:17 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 31 #include <atf-c.h> 32 #include <stdint.h> 33 34 #include "hash.h" 35 36 /* known-answer tests */ 37 struct kat { 38 const char *in; 39 unsigned long long out; 40 }; 41 42 /* 43 * From the SysV spec, with uint64_t instead of unsigned long to 44 * illustrate the problem on all systems, not just LP64 ones. 45 * 46 * https://www.sco.com/developers/devspecs/gabi41.pdf#page=95 47 * https://web.archive.org/web/20241216221811/https://www.sco.com/developers/devspecs/gabi41.pdf#page=95 48 */ 49 static uint64_t 50 sysv_broken_hash(const char *name) 51 { 52 uint64_t h = 0, g; 53 54 while (*name) { 55 h = (h << 4) + *name++; 56 if ((g = h & 0xf0000000) != 0) 57 h ^= g >> 24; 58 h &= ~g; 59 } 60 61 return h; 62 } 63 64 ATF_TC(sysv); 65 ATF_TC_HEAD(sysv, tc) 66 { 67 atf_tc_set_md_var(tc, "descr", "SysV hash (32-bit)"); 68 } 69 ATF_TC_BODY(sysv, tc) 70 { 71 static const struct kat kat[] = { 72 { "", 0x00000000 }, 73 { "a", 0x00000061 }, 74 { "aa", 0x00000671 }, 75 { "aaa", 0x00006771 }, 76 { "aaaa", 0x00067771 }, 77 { "aaaaa", 0x00677771 }, 78 { "aaaaaa", 0x06777771 }, 79 { "aaaaaaa", 0x07777711 }, 80 { "aaaaaaaa", 0x07777101 }, 81 { "aaaaaaaaa", 0x07771001 }, 82 { "ab", 0x00000672 }, 83 { "abc", 0x00006783 }, 84 { "abcd", 0x00067894 }, 85 { "abcde", 0x006789a5 }, 86 { "abcdef", 0x06789ab6 }, 87 { "abcdefg", 0x0789aba7 }, 88 { "abcdefgh", 0x089abaa8 }, 89 { "abcdefghi", 0x09abaa69 }, 90 /* https://maskray.me/blog/2023-04-12-elf-hash-function */ 91 { "Z", 0x0000005a }, 92 { "ZZ", 0x000005fa }, 93 { "ZZZ", 0x00005ffa }, 94 { "ZZZZ", 0x0005fffa }, 95 { "ZZZZZ", 0x005ffffa }, 96 { "ZZZZZW", 0x05fffff7 }, 97 { "ZZZZZW9", 0x0ffffff9 }, 98 { "ZZZZZW9p", 0x00000000 }, 99 { "pneumonoultramicroscopicsilicovolcanoconiosis", 100 0x051706b3 }, 101 }; 102 unsigned i; 103 104 for (i = 0; i < __arraycount(kat); i++) { 105 unsigned long long h = _rtld_sysv_hash(kat[i].in); 106 107 ATF_CHECK_EQ_MSG(h, kat[i].out, 108 "[%u] _rtld_hash_sysv(\"%s\") = 0x%08llx != 0x%08llx", 109 i, kat[i].in, h, kat[i].out); 110 } 111 } 112 113 ATF_TC(sysv_broken); 114 ATF_TC_HEAD(sysv_broken, tc) 115 { 116 atf_tc_set_md_var(tc, "descr", 117 "SysV hash (broken with 64-bit unsigned long)"); 118 } 119 ATF_TC_BODY(sysv_broken, tc) 120 { 121 static const struct kat kat[] = { 122 { "", 0x00000000 }, 123 { "a", 0x00000061 }, 124 { "aa", 0x00000671 }, 125 { "aaa", 0x00006771 }, 126 { "aaaa", 0x00067771 }, 127 { "aaaaa", 0x00677771 }, 128 { "aaaaaa", 0x06777771 }, 129 { "aaaaaaa", 0x07777711 }, 130 { "aaaaaaaa", 0x07777101 }, 131 { "aaaaaaaaa", 0x07771001 }, 132 { "ab", 0x00000672 }, 133 { "abc", 0x00006783 }, 134 { "abcd", 0x00067894 }, 135 { "abcde", 0x006789a5 }, 136 { "abcdef", 0x06789ab6 }, 137 { "abcdefg", 0x0789aba7 }, 138 { "abcdefgh", 0x089abaa8 }, 139 { "abcdefghi", 0x09abaa69 }, 140 /* https://maskray.me/blog/2023-04-12-elf-hash-function */ 141 { "Z", 0x0000005a }, 142 { "ZZ", 0x000005fa }, 143 { "ZZZ", 0x00005ffa }, 144 { "ZZZZ", 0x0005fffa }, 145 { "ZZZZZ", 0x005ffffa }, 146 { "ZZZZZW", 0x05fffff7 }, 147 { "ZZZZZW9", 0x0ffffff9 }, 148 { "ZZZZZW9p", 0x100000000 }, 149 { "pneumonoultramicroscopicsilicovolcanoconiosis", 150 0x051706b3 }, 151 }; 152 unsigned i; 153 154 for (i = 0; i < __arraycount(kat); i++) { 155 unsigned long long h = sysv_broken_hash(kat[i].in); 156 157 ATF_CHECK_EQ_MSG(h, kat[i].out, 158 "[%u] sysv_broken_hash(\"%s\") = 0x%08llx != 0x%08llx", 159 i, kat[i].in, h, kat[i].out); 160 } 161 } 162 163 ATF_TC(gnu); 164 ATF_TC_HEAD(gnu, tc) 165 { 166 atf_tc_set_md_var(tc, "descr", "GNU hash (djb2)"); 167 } 168 ATF_TC_BODY(gnu, tc) 169 { 170 static const struct kat kat[] = { 171 { """", 0x00001505 }, 172 { "a", 0x0002b606 }, 173 { "aa", 0x00597727 }, 174 { "aaa", 0x0b885c68 }, 175 { "aaaa", 0x7c93e9c9 }, 176 { "aaaaa", 0x0f11234a }, 177 { "aaaaaa", 0xf1358ceb }, 178 { "aaaaaaa", 0x17e72aac }, 179 { "aaaaaaaa", 0x14cc808d }, 180 { "aaaaaaaaa", 0xae5c928e }, 181 { "ab", 0x00597728 }, 182 { "abc", 0x0b885c8b }, 183 { "abcd", 0x7c93ee4f }, 184 { "abcde", 0x0f11b894 }, 185 { "abcdef", 0xf148cb7a }, 186 { "abcdefg", 0x1a623b21 }, 187 { "abcdefgh", 0x66a99fa9 }, 188 { "abcdefghi", 0x3bdd9532 }, 189 { "Z", 0x0002b5ff }, 190 { "ZZ", 0x00597639 }, 191 { "ZZZ", 0x0b883db3 }, 192 { "ZZZZ", 0x7c8ff46d }, 193 { "ZZZZZ", 0x0e8e8267 }, 194 { "ZZZZZW", 0xe05ecf9e }, 195 { "ZZZZZW9", 0xec38c397 }, 196 { "ZZZZZW9p", 0x735136e7 }, 197 { "pneumonoultramicroscopicsilicovolcanoconiosis", 198 0xee6245b5 }, 199 }; 200 unsigned i; 201 202 for (i = 0; i < __arraycount(kat); i++) { 203 unsigned long long h = _rtld_gnu_hash(kat[i].in); 204 205 ATF_CHECK_EQ_MSG(h, kat[i].out, 206 "[%u] _rtld_gnu_hash(\"%s\") = 0x%08llx != 0x%08llx", 207 i, kat[i].in, h, kat[i].out); 208 } 209 } 210 211 ATF_TP_ADD_TCS(tp) 212 { 213 214 ATF_TP_ADD_TC(tp, gnu); 215 ATF_TP_ADD_TC(tp, sysv); 216 ATF_TP_ADD_TC(tp, sysv_broken); 217 218 return atf_no_error(); 219 } 220