DF-5342 / htfix_mask_truncation.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | /* * DF-5342 โ u_int truncation of the HT-fixup APIC-id mask for ids 32..63 * (mptable.c:641,658-659,784) makes mptable_hyperthread_fixup() decide on * partial data and enumerate phantom CPUs. * * pass1 builds: u_int ht_apicid_mask |= 1UL << ent->apic_id; (id < 64) * For 32 <= id < 64 the 64-bit 1UL<<id truncates to 0 when stored in the * u_int โ the id silently vanishes from the mask while ht_fixup stays * ENABLED (disable only happens for id >= 64, mptable.c:660-663). * mask.ary[0] = ht_apicid_mask (mptable.c:784) then feeds * mptable_hyperthread_fixup() whose distance/boundary checks run over the * partial set. arg1.cpu_count *= logical_cpus (mptable.c:787) inflates * naps and pass2 synthesizes fake PROC entries with APIC ids that do not * exist -> start_all_aps() SIPIs phantom ids and the boot waits forever * on smp_startup_mask (mp_machdep.c:595). * * Demonstrated with a table listing ids {0, 2, 33} on an HTT CPU with * lcpus_max = 2: * - with the in-tree u_int mask: ids {0,2} -> dist=2 -> fixup returns 2 * -> cpu_count 3*2=6, naps=5, fakes 1,3,34 enumerated (phantoms). * - with a non-truncating 64-bit mask: distances 2 and 31 differ -> * fixup returns 0 (the intended "kill the fixup" decision). * * mptable_hyperthread_fixup() body is verbatim (mptable.c:400-498); * CPUMASK helpers mirror sys/cpu/x86_64/include/cpumask.h (MAXCPU=256, * ary[4], word index masked & 3). cpu_feature/vendor plumbing is * pre-applied (HTT present, lcpus_max already power-of-2) as noted. */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <stdlib.h> typedef unsigned char u_char; typedef unsigned int u_int; #define MAXCPU 256 #define CPUMASK_TESTBIT(m, i) ((((m).ary[((i) >> 6) & 3]) >> ((i) & 63)) & 1) struct cpumask { uint64_t ary[4]; }; static void kassert(int ok) { if (!ok) { fprintf(stderr, "KKASSERT fired\n"); exit(86); } } /* ---- verbatim: mptable_hyperthread_fixup (mptable.c:400-498) ---- * (HTT-present and Intel pow2-roundup pre-applied; lcpus_max passed in) */ static int mptable_hyperthread_fixup(struct cpumask id_mask, int cpu_count, int lcpus_max) { int i, id, logical_cpus; kassert(cpu_count != 0); /* mptable.c:429 */ if (cpu_count == lcpus_max) return 0; else if (cpu_count == 1) logical_cpus = lcpus_max; else { int cur, prev, dist; dist = cur = prev = -1; for (id = 0; id < MAXCPU; ++id) { /* mptable.c:446 */ if (CPUMASK_TESTBIT(id_mask, id) == 0) continue; cur = id; if (prev >= 0) { int new_dist = cur - prev; if (dist < 0) dist = new_dist; if (dist != new_dist) /* mptable.c:462 */ return 0; } prev = cur; } if (dist == 1) /* mptable.c:467 */ return 0; if (dist & (dist - 1)) /* mptable.c:471: must be pow2 */ return 0; if (dist > lcpus_max) logical_cpus = lcpus_max; else logical_cpus = dist; } for (id = 0; id < MAXCPU; id++) { /* mptable.c:487 */ if (CPUMASK_TESTBIT(id_mask, id) == 0) continue; if (id % logical_cpus != 0) return 0; for (i = id + 1; i < id + logical_cpus; i++) if (CPUMASK_TESTBIT(id_mask, i) != 0) return 0; } return logical_cpus; } /* ---- verbatim: pass1 mask build (mptable.c:654-664) ---- */ static u_int build_mask_u_int(const u_char ids[], int n, int *cpu_count, int *ht_fixup) { u_int ht_apicid_mask = 0; int i; *cpu_count = 0; *ht_fixup = 1; for (i = 0; i < n; i++) { (*cpu_count)++; if (ids[i] < 64) ht_apicid_mask |= 1UL << ids[i]; /* mptable.c:659 โ truncates for id >= 32 */ else if (*ht_fixup) *ht_fixup = 0; } return ht_apicid_mask; } static uint64_t build_mask_true(const u_char ids[], int n) { uint64_t m = 0; int i; for (i = 0; i < n; i++) if (ids[i] < 64) m |= 1ULL << ids[i]; return m; } static void run(const char *name, const u_char ids[], int n, int lcpus_max) { struct cpumask mask; u_int m_ui; uint64_t m_true; int cpu_count, ht_fixup, fix, i; m_ui = build_mask_u_int(ids, n, &cpu_count, &ht_fixup); m_true = build_mask_true(ids, n); /* in-tree path: mask.ary[0] = (u_int)ht_apicid_mask (mptable.c:784) */ memset(&mask, 0, sizeof(mask)); mask.ary[0] = m_ui; fix = ht_fixup ? mptable_hyperthread_fixup(mask, cpu_count, lcpus_max) : 0; printf("== %s ==\n", name); printf(" ids present : {"); for (i = 0; i < n; i++) printf(" %u", ids[i]); printf(" } cpu_count=%d lcpus_max=%d\n", cpu_count, lcpus_max); printf(" in-tree u_int mask=0x%x ", m_ui); if (fix) { printf("-> fixup ENGAGES: logical_cpus=%d\n" " cpu_count *= logical_cpus -> %d, naps = %d\n" " pass2 fakes enumerated for EVERY entry: apic ids {", fix, cpu_count * fix, cpu_count * fix - 1); for (i = 0; i < n; i++) { int j; for (j = 1; j < fix; j++) printf(" %u", (u_char)(ids[i] + j)); } printf(" } (phantom ids)\n"); } else { printf("-> fixup killed (return 0)\n"); } /* true 64-bit mask, same inputs */ memset(&mask, 0, sizeof(mask)); mask.ary[0] = m_true; fix = mptable_hyperthread_fixup(mask, cpu_count, lcpus_max); printf(" true 64-bit mask=0x%llx -> fixup returns %d%s\n", (unsigned long long)m_true, fix, fix ? " (ENGAGES)" : " (killed โ the intended decision)"); } int main(void) { const u_char ids1[] = { 0, 2, 33 }; const u_char ids2[] = { 0, 2 }; /* control: truncation irrelevant */ const u_char ids3[] = { 0, 33, 34 }; /* id>=32 group spaced */ run("trigger table: ids {0,2,33}, lcpus_max=2", ids1, 3, 2); run("control table: ids {0,2}, lcpus_max=2", ids2, 2, 2); run("trigger table: ids {0,33,34}, lcpus_max=2", ids3, 3, 2); return 0; } |