From f766c8f61f5e248b1a82a48899b0debaa6a176c8 Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Fri, 21 Aug 2026 17:40:57 +0800 Subject: [PATCH] Record NUMA nodes >= 32 in the partition node bitmap InitNumaTopology builds partition_to_nodes by OR-ing in 1 << node, where node is a size_t. The literal 1 is a 32-bit signed int, so for node >= 31 the shift is undefined behavior and, on x86, corrupts the uint64_t bitmap: node 31 sign-extends to set bits 31..63, and node 32+ has its shift count masked modulo 32, setting a bit belonging to a lower node instead. The resulting nodemask is passed to mbind() for memory binding, so on machines with 32 or more NUMA nodes memory could be bound to the wrong nodes or mbind could fail (fatal under strict binding). Shift in a 64-bit type instead and add a regression test covering a node index of 32. --- tcmalloc/internal/numa.cc | 2 +- tcmalloc/internal/numa_test.cc | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tcmalloc/internal/numa.cc b/tcmalloc/internal/numa.cc index c9d69d0d7..4d7804dc5 100644 --- a/tcmalloc/internal/numa.cc +++ b/tcmalloc/internal/numa.cc @@ -126,7 +126,7 @@ bool InitNumaTopology(size_t cpu_to_scaled_partition[kMaxCpus], // Record this node in partition_to_nodes. const size_t partition = NodeToPartition(node, num_partitions); - partition_to_nodes[partition] |= 1 << node; + partition_to_nodes[partition] |= uint64_t{1} << node; // cpu_to_scaled_partition_ entries are default initialized to zero, so // skip redundantly parsing CPU lists for nodes that map to partition 0. diff --git a/tcmalloc/internal/numa_test.cc b/tcmalloc/internal/numa_test.cc index 5a0ee3392..bfd04d6b1 100644 --- a/tcmalloc/internal/numa_test.cc +++ b/tcmalloc/internal/numa_test.cc @@ -233,6 +233,24 @@ TEST_F(NumaTopologyTest, LongCpuLists) { } } +// Ensure that NUMA nodes with an index >= 32 are recorded in the partition +// bitmap. partition_to_nodes is a uint64_t bitmap, so the node index must be +// shifted in a 64-bit type; shifting a 32-bit int by >= 31 would be undefined +// behavior and would drop (or corrupt) the bit for such nodes. +TEST_F(NumaTopologyTest, HighNodeIndex) { + // 33 nodes (indices 0..32). Node 32 maps to partition 0 (32 % 4 == 0) and + // must set bit 32 of the partition 0 node bitmap. + std::vector nodes; + for (size_t node = 0; node < 33; ++node) { + nodes.emplace_back(absl::StrCat(node)); + } + + const auto nt = CreateNumaTopology<4>(nodes); + + EXPECT_EQ(nt.numa_aware(), true); + EXPECT_EQ(nt.GetPartitionNodes(0) & (uint64_t{1} << 32), uint64_t{1} << 32); +} + // Ensure we can initialize using the host system's real NUMA topology // information. TEST_F(NumaTopologyTest, Host) {