Bug Summary

File:root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc
Warning:line 254, column 26
Forming reference to null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name map.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/toolkit/components/protobuf -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/toolkit/components/protobuf -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D GOOGLE_PROTOBUF_NO_RTTI -D GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER -D HAVE_PTHREAD -D HAVE_ZLIB -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/toolkit/components/protobuf -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/toolkit/components/protobuf -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/protobuf/third_party/utf8_range -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0/unix-print -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/cloudproviders -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/harfbuzz -I /usr/include/atk-1.0 -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glycin-2 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-ignored-qualifiers -Wno-maybe-uninitialized -Wno-return-type -Wno-sign-compare -Wno-subobject-linkage -Wno-unused-function -Wno-comma -Wno-null-conversion -Wno-unused-local-typedef -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file or at
6// https://developers.google.com/open-source/licenses/bsd
7
8#include "google/protobuf/map.h"
9
10#include <algorithm>
11#include <atomic>
12#include <cstddef>
13#include <cstdint>
14#include <string>
15
16#include "absl/base/no_destructor.h"
17#include "absl/base/optimization.h"
18#include "absl/functional/overload.h"
19#include "absl/log/absl_check.h"
20#include "google/protobuf/arena.h"
21#include "google/protobuf/field_with_arena.h"
22#include "google/protobuf/message_lite.h"
23#include "google/protobuf/port.h"
24
25
26// Must be included last.
27#include "google/protobuf/port_def.inc"
28
29namespace google {
30namespace protobuf {
31namespace internal {
32
33std::atomic<MapFieldBaseForParse::SyncFunc>
34 MapFieldBaseForParse::sync_map_with_repeated{};
35
36NodeBase* const kGlobalEmptyTable[kGlobalEmptyTableSize] = {};
37
38void UntypedMapBase::UntypedMergeFrom(Arena* arena,
39 const UntypedMapBase& other) {
40 ABSL_DCHECK_EQ(arena, this->arena())while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_EQImpl( ::absl::log_internal
::GetReferenceableValue((arena)), ::absl::log_internal::GetReferenceableValue
((this->arena())), ("arena" " " "==" " " "this->arena()"
))) switch (0) case 0: default: !(true) ? (void)0 : ::absl::log_internal
::Voidify() && ::absl::log_internal::LogMessageFatal(
"/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 40, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
41 if (other.empty()) return;
42
43 // Do the merging in steps to avoid Key*Value number of instantiations and
44 // reduce code duplication per instantation.
45 NodeBase* nodes = nullptr;
46
47 // First, allocate all the nodes without types.
48 for (size_t i = 0; i < other.num_elements_; ++i) {
49 NodeBase* new_node = AllocNode(arena);
50 new_node->next = nodes;
51 nodes = new_node;
52 }
53
54 // Then, copy the values.
55 VisitValueType([&](auto value_type) {
56 using Value = typename decltype(value_type)::type;
57 NodeBase* out_node = nodes;
58
59 // Get the ClassData once to avoid redundant virtual function calls.
60 const internal::ClassData* class_data =
61 std::is_same_v<MessageLite, Value>
62 ? GetClassData(*other.GetValue<MessageLite>(other.begin().node_))
63 : nullptr;
64
65 for (auto it = other.begin(); !it.Equals(EndIterator()); it.PlusPlus()) {
66 Value* out = GetValue<Value>(out_node);
67 out_node = out_node->next;
68 auto& in = *other.GetValue<Value>(it.node_);
69 if constexpr (std::is_same_v<MessageLite, Value>) {
70 class_data->PlacementNew(out, arena)->CheckTypeAndMergeFrom(in);
71 } else {
72 Arena::CreateInArenaStorage(out, arena, in);
73 }
74 }
75 });
76
77 // Finally, copy the keys and insert the nodes.
78 VisitKeyType([&](auto key_type) {
79 using Key = typename decltype(key_type)::type;
80 for (auto it = other.begin(); !it.Equals(EndIterator()); it.PlusPlus()) {
81 NodeBase* node = nodes;
82 nodes = nodes->next;
83 const Key& in = *other.GetKey<Key>(it.node_);
84 Key* out = GetKey<Key>(node);
85 if (!internal::InitializeMapKey(out, in, arena)) {
86 Arena::CreateInArenaStorage(out, arena, in);
87 }
88
89 static_cast<KeyMapBase<Key>*>(this)->InsertOrReplaceNode(
90 arena, static_cast<typename KeyMapBase<Key>::KeyNode*>(node));
91 }
92 });
93}
94
95void UntypedMapBase::UntypedSwap(Arena* arena, UntypedMapBase& other,
96 Arena* other_arena) {
97 ABSL_DCHECK_EQ(arena, this->arena())while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_EQImpl( ::absl::log_internal
::GetReferenceableValue((arena)), ::absl::log_internal::GetReferenceableValue
((this->arena())), ("arena" " " "==" " " "this->arena()"
))) switch (0) case 0: default: !(true) ? (void)0 : ::absl::log_internal
::Voidify() && ::absl::log_internal::LogMessageFatal(
"/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 97, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
98 ABSL_DCHECK_EQ(other_arena, other.arena())while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_EQImpl( ::absl::log_internal
::GetReferenceableValue((other_arena)), ::absl::log_internal::
GetReferenceableValue((other.arena())), ("other_arena" " " "=="
" " "other.arena()"))) switch (0) case 0: default: !(true) ?
(void)0 : ::absl::log_internal::Voidify() && ::absl::
log_internal::LogMessageFatal("/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 98, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
99
100 if (arena == other_arena) {
101 InternalSwap(&other);
102 } else {
103 // `FieldWithArena` checks that the arena pointer is null when destroying a
104 // destructor-skippable type. Since `UntypedMapBase` is
105 // destructor-skippable, we need to put it in an `absl::NoDestructor` and
106 // manually destroy it if the arena pointer is null.
107 absl::NoDestructor<FieldWithArena<UntypedMapBase>> tmp_container(
108 arena, type_info_);
109 UntypedMapBase& tmp = tmp_container->field();
110 InternalSwap(&tmp);
111
112 ABSL_DCHECK(empty())switch (0) case 0: default: !((__builtin_expect(false || (!((
empty()))), false))) ? (void)0 : ::absl::log_internal::Voidify
() && ::absl::log_internal::LogMessageFatal("/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 112, "empty()").InternalStream()
;
113 UntypedMergeFrom(arena, other);
114
115 other.ClearTable(other_arena, /*reset=*/true);
116 other.UntypedMergeFrom(other_arena, tmp);
117
118 if (arena == nullptr) {
119 tmp.ClearTable(arena, /*reset=*/false);
120 tmp.~UntypedMapBase();
121 }
122 }
123}
124
125void UntypedMapBase::DeleteNode(NodeBase* node) {
126 const auto destroy = absl::Overload{
127 [](std::string* str) { str->~basic_string(); },
128 [](MessageLite* msg) { msg->DestroyInstance(); }, [](void*) {}};
129 VisitKey(node, destroy);
130 VisitValue(node, destroy);
131 DeallocNode(node);
132}
133
134void UntypedMapBase::DeleteList(NodeBase* list) {
135 while (list != nullptr) {
136 NodeBase* n = list;
137 list = list->next;
138 DeleteNode(n);
139 }
140}
141
142void UntypedMapBase::ClearTableImpl(Arena* arena, bool reset) {
143 ABSL_DCHECK_NE(num_buckets_, kGlobalEmptyTableSize)while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_NEImpl( ::absl::log_internal
::GetReferenceableValue((num_buckets_)), ::absl::log_internal
::GetReferenceableValue((kGlobalEmptyTableSize)), ("num_buckets_"
" " "!=" " " "kGlobalEmptyTableSize"))) switch (0) case 0: default
: !(true) ? (void)0 : ::absl::log_internal::Voidify() &&
::absl::log_internal::LogMessageFatal("/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 143, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
144 ABSL_DCHECK_EQ(arena, this->arena())while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_EQImpl( ::absl::log_internal
::GetReferenceableValue((arena)), ::absl::log_internal::GetReferenceableValue
((this->arena())), ("arena" " " "==" " " "this->arena()"
))) switch (0) case 0: default: !(true) ? (void)0 : ::absl::log_internal
::Voidify() && ::absl::log_internal::LogMessageFatal(
"/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 144, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
145
146 if (arena == nullptr) {
147 const auto loop = [this](auto destroy_node) {
148 NodeBase** table = table_;
149 for (map_index_t b = 0, end = num_buckets_; b < end; ++b) {
150 for (NodeBase* node = table[b]; node != nullptr;) {
151 NodeBase* next = node->next;
152 absl::PrefetchToLocalCacheNta(next);
153 destroy_node(node);
154 SizedDelete(node, type_info_.node_size);
155 node = next;
156 }
157 }
158 };
159
160 const auto dispatch_key = [&](auto value_handler) {
161 if (type_info_.key_type_kind() < TypeKind::kString) {
162 loop(value_handler);
163 } else if (type_info_.key_type_kind() == TypeKind::kString) {
164 loop([=](NodeBase* node) {
165 static_cast<std::string*>(node->GetVoidKey())->~basic_string();
166 value_handler(node);
167 });
168 } else {
169 Unreachable();
170 }
171 };
172
173 if (type_info_.value_type_kind() < TypeKind::kString) {
174 dispatch_key([](NodeBase*) {});
175 } else if (type_info_.value_type_kind() == TypeKind::kString) {
176 dispatch_key([&](NodeBase* node) {
177 GetValue<std::string>(node)->~basic_string();
178 });
179 } else if (type_info_.value_type_kind() == TypeKind::kMessage) {
180 dispatch_key([&](NodeBase* node) {
181 GetValue<MessageLite>(node)->DestroyInstance();
182 });
183 } else {
184 Unreachable();
185 }
186 }
187
188 if (reset) {
189 std::fill(table_, table_ + num_buckets_, nullptr);
190 num_elements_ = 0;
191 } else {
192 DeleteTable(arena, table_, num_buckets_);
193 }
194}
195
196size_t UntypedMapBase::SpaceUsedExcludingSelfLong() const {
197 size_t size = 0;
198 // The size of the table.
199 size += sizeof(void*) * num_buckets_;
200 // All the nodes.
201 size += type_info_.node_size * num_elements_;
202 VisitAllNodes([&](auto* key, auto* value) {
203 const auto space_used = absl::Overload{
204 [](const std::string* str) -> size_t {
205 return StringSpaceUsedExcludingSelfLong(*str);
206 },
207 [&](const MessageLite* msg) -> size_t {
208 const auto* class_data = GetClassData(*msg);
209 if (class_data->is_lite) return 0;
210 return class_data->full().descriptor_methods()->space_used_long(
211 *msg) -
212 class_data->allocation_size();
213 },
214 [](const void*) -> size_t { return 0; }};
215 size += space_used(key);
216 size += space_used(value);
217 });
218 return size;
219}
220
221static size_t AlignTo(size_t v, size_t alignment, size_t& max_align) {
222 max_align = std::max<size_t>(max_align, alignment);
223 return (v + alignment - 1) / alignment * alignment;
224}
225
226struct Offsets {
227 size_t start;
228 size_t end;
229};
230
231template <typename T>
232static Offsets AlignAndAddSize(size_t v, size_t& max_align) {
233 v = AlignTo(v, alignof(T), max_align);
234 return {v, v + sizeof(T)};
235}
236
237static Offsets AlignAndAddSizeDynamic(
238 size_t v, UntypedMapBase::TypeKind kind,
239 const MessageLite* value_prototype_if_message, size_t& max_align) {
240 switch (kind) {
3
Control jumps to 'case kMessage:' at line 253
241 case UntypedMapBase::TypeKind::kBool:
242 return AlignAndAddSize<bool>(v, max_align);
243 case UntypedMapBase::TypeKind::kU32:
244 return AlignAndAddSize<int32_t>(v, max_align);
245 case UntypedMapBase::TypeKind::kU64:
246 return AlignAndAddSize<int64_t>(v, max_align);
247 case UntypedMapBase::TypeKind::kFloat:
248 return AlignAndAddSize<float>(v, max_align);
249 case UntypedMapBase::TypeKind::kDouble:
250 return AlignAndAddSize<double>(v, max_align);
251 case UntypedMapBase::TypeKind::kString:
252 return AlignAndAddSize<std::string>(v, max_align);
253 case UntypedMapBase::TypeKind::kMessage: {
254 auto* class_data = GetClassData(*value_prototype_if_message);
4
Forming reference to null pointer
255 v = AlignTo(v, class_data->alignment(), max_align);
256 return {v, v + class_data->allocation_size()};
257 }
258 default:
259 Unreachable();
260 }
261}
262
263template <typename T, typename U>
264T Narrow(U value) {
265 ABSL_CHECK_EQ(value, static_cast<T>(value))while (const char* absl_log_internal_check_op_result [[maybe_unused
]] = ::absl::log_internal::Check_EQImpl( ::absl::log_internal
::GetReferenceableValue((value)), ::absl::log_internal::GetReferenceableValue
((static_cast<T>(value))), ("value" " " "==" " " "static_cast<T>(value)"
))) switch (0) case 0: default: !(true) ? (void)0 : ::absl::log_internal
::Voidify() && ::absl::log_internal::LogMessageFatal(
"/root/firefox-clang/toolkit/components/protobuf/src/google/protobuf/map.cc"
, 265, ::absl::implicit_cast<const char* >( absl_log_internal_check_op_result
)) .InternalStream()
;
266 return static_cast<T>(value);
267}
268
269UntypedMapBase::TypeInfo UntypedMapBase::GetTypeInfoDynamic(
270 TypeKind key_type, TypeKind value_type,
271 const MessageLite* value_prototype_if_message) {
272 size_t max_align = alignof(NodeBase);
273 const auto key_offsets =
274 AlignAndAddSizeDynamic(sizeof(NodeBase), key_type, nullptr, max_align);
1
Passing null pointer value via 3rd parameter 'value_prototype_if_message'
2
Calling 'AlignAndAddSizeDynamic'
275 const auto value_offsets = AlignAndAddSizeDynamic(
276 key_offsets.end, value_type, value_prototype_if_message, max_align);
277 return TypeInfo{
278 Narrow<uint16_t>(AlignTo(value_offsets.end, max_align, max_align)),
279 Narrow<uint8_t>(value_offsets.start), static_cast<uint8_t>(key_type),
280 static_cast<uint8_t>(value_type)};
281}
282
283void UntypedMapBase::InsertOrReplaceNodes(Arena* arena, NodeBase* list,
284 map_index_t count) {
285 if (ABSL_PREDICT_FALSE(count == 0)(__builtin_expect(false || (count == 0), false))) return;
286 VisitKeyType([this, arena, list, count](auto key_type) {
287 using Key = typename decltype(key_type)::type;
288 static_cast<KeyMapBase<Key>&>(*this).InsertOrReplaceNodes(
289 arena, static_cast<typename KeyMapBase<Key>::KeyNode*>(list), count);
290 });
291}
292
293} // namespace internal
294} // namespace protobuf
295} // namespace google
296
297#include "google/protobuf/port_undef.inc"