Bug Summary

File:root/firefox-clang/memory/replace/dmd/test/SmokeDMD.cpp
Warning:line 85, column 3
1st function call argument is an uninitialized value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O0 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SmokeDMD.cpp -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/memory/replace/dmd/test -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/replace/dmd/test -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 -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 MOZ_HAS_MOZGLUE -D MOZ_NO_MOZALLOC -I /root/firefox-clang/memory/replace/dmd/test -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/memory/replace/dmd/test -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 -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 -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 -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-10-17-231001-351276-1 -x c++ /root/firefox-clang/memory/replace/dmd/test/SmokeDMD.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// This program is used by the DMD xpcshell test. It is run under DMD and
6// produces some output. The xpcshell test then post-processes and checks this
7// output.
8//
9// Note that this file does not have "Test" or "test" in its name, because that
10// will cause the build system to not record breakpad symbols for it, which
11// will stop the post-processing (which includes stack fixing) from working
12// correctly.
13
14// This is required on some systems such as Fedora to allow
15// building with -O0 together with --warnings-as-errors due to
16// a check in /usr/include/features.h
17#undef _FORTIFY_SOURCE
18
19#include <errno(*__errno_location ()).h>
20#include <stdio.h>
21#include <stdlib.h>
22
23#include <string>
24
25#include "mozilla/Assertions.h"
26#include "mozilla/JSONWriter.h"
27#include "mozilla/Sprintf.h"
28#include "mozilla/UniquePtr.h"
29#include "DMD.h"
30
31using mozilla::MakeUnique;
32using namespace mozilla::dmd;
33
34MOZ_RUNINIT DMDFuncs::Singleton DMDFuncs::sSingleton;
35
36// The directory the JSON files are written to.
37static const char* sOutputDir = ".";
38
39class FpWriteFunc final : public mozilla::JSONWriteFunc {
40 public:
41 explicit FpWriteFunc(const char* aFilename) {
42 std::string path = std::string(sOutputDir) + "/" + aFilename;
43 mFp = fopen(path.c_str(), "w");
44 if (!mFp) {
45 fprintf(stderrstderr, "SmokeDMD: can't create %s file: %s\n", path.c_str(),
46 strerror(errno(*__errno_location ())));
47 exit(1);
48 }
49 }
50
51 ~FpWriteFunc() { fclose(mFp); }
52
53 void Write(const mozilla::Span<const char>& aStr) final {
54 for (const char c : aStr) {
55 fputc(c, mFp);
56 }
57 }
58
59 private:
60 FILE* mFp;
61};
62
63// This stops otherwise-unused variables from being optimized away.
64static void UseItOrLoseIt(void* aPtr, int aSeven) {
65 char buf[64];
66 int n = SprintfLiteral(buf, "%p\n", aPtr);
67 if (n == 20 + aSeven) {
68 fprintf(stderrstderr, "well, that is surprising");
69 }
70}
71
72// This function checks that heap blocks that have the same stack trace but
73// different (or no) reporters get aggregated separately.
74void Foo(int aSeven) {
75 char* a[6];
76 for (int i = 0; i < aSeven - 1; i++) {
1
Assuming the condition is false
2
Loop condition is false. Execution continues on line 85
77 a[i] = (char*)malloc(128 - 16 * i);
78 UseItOrLoseIt(a[i], aSeven);
79 }
80
81 // Oddly, some versions of clang will cause identical stack traces to be
82 // generated for adjacent calls to Report(), which breaks the test. Inserting
83 // the UseItOrLoseIt() calls in between is enough to prevent this.
84
85 Report(a[2]); // reported
3
1st function call argument is an uninitialized value
86
87 UseItOrLoseIt(a[2], aSeven);
88
89 for (int i = 0; i < aSeven - 5; i++) {
90 Report(a[i]); // reported
91 UseItOrLoseIt(a[i], aSeven);
92 }
93
94 UseItOrLoseIt(a[2], aSeven);
95
96 Report(a[3]); // reported
97
98 // a[4], a[5] unreported
99}
100
101void TestEmpty(const char* aTestName, const char* aMode) {
102 char filename[128];
103 SprintfLiteral(filename, "complete-%s-%s.json", aTestName, aMode);
104 auto f = MakeUnique<FpWriteFunc>(filename);
105
106 char options[128];
107 SprintfLiteral(options, "--mode=%s --stacks=full", aMode);
108 ResetEverything(options);
109
110 // Zero for everything.
111 Analyze(std::move(f));
112}
113
114void TestFull(const char* aTestName, int aNum, const char* aMode, int aSeven) {
115 char filename[128];
116 SprintfLiteral(filename, "complete-%s%d-%s.json", aTestName, aNum, aMode);
117 auto f = MakeUnique<FpWriteFunc>(filename);
118
119 // The --show-dump-stats=yes is there just to give that option some basic
120 // testing, e.g. ensure it doesn't crash. It's hard to test much beyond that.
121 char options[128];
122 SprintfLiteral(options, "--mode=%s --stacks=full --show-dump-stats=yes",
123 aMode);
124 ResetEverything(options);
125
126 // Analyze 1: 1 freed, 9 out of 10 unreported.
127 // Analyze 2: still present and unreported.
128 int i;
129 char* a = nullptr;
130 for (i = 0; i < aSeven + 3; i++) {
131 a = (char*)malloc(100);
132 UseItOrLoseIt(a, aSeven);
133 }
134 free(a);
135
136 // A no-op.
137 free(nullptr);
138
139 // Note: 16 bytes is the smallest requested size that gives consistent
140 // behaviour across all platforms with jemalloc.
141 // Analyze 1: reported.
142 // Analyze 2: thrice-reported.
143 char* a2 = (char*)malloc(16);
144 Report(a2);
145
146 // Analyze 1: reported.
147 // Analyze 2: reportedness carries over, due to ReportOnAlloc.
148 char* b = (char*)malloc(10);
149 ReportOnAlloc(b);
150
151 // ReportOnAlloc, then freed.
152 // Analyze 1: freed, irrelevant.
153 // Analyze 2: freed, irrelevant.
154 char* b2 = (char*)malloc(16);
155 ReportOnAlloc(b2);
156 free(b2);
157
158 // Analyze 1: reported 4 times.
159 // Analyze 2: freed, irrelevant.
160 char* c = (char*)calloc(10, 3);
161 Report(c);
162 for (int i = 0; i < aSeven - 4; i++) {
163 Report(c);
164 }
165
166 // Analyze 1: ignored.
167 // Analyze 2: irrelevant.
168 Report((void*)(intptr_t)i);
169
170 // jemalloc rounds this up to 8192.
171 // Analyze 1: reported.
172 // Analyze 2: freed.
173 char* e = (char*)malloc(4096);
174 e = (char*)realloc(e, 7169);
175 Report(e);
176
177 // First realloc is like malloc; second realloc is shrinking.
178 // Analyze 1: reported.
179 // Analyze 2: re-reported.
180 char* e2 = (char*)realloc(nullptr, 1024);
181 e2 = (char*)realloc(e2, 512);
182 Report(e2);
183
184 // First realloc is like malloc; second realloc creates a min-sized block.
185 // XXX: on Windows, second realloc frees the block.
186 // Analyze 1: reported.
187 // Analyze 2: freed, irrelevant.
188 char* e3 = (char*)realloc(nullptr, 1023);
189 // e3 = (char*) realloc(e3, 0);
190 MOZ_ASSERT(e3)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(e3)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(e3))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("e3", "/root/firefox-clang/memory/replace/dmd/test/SmokeDMD.cpp"
, 190); AnnotateMozCrashReason("MOZ_ASSERT" "(" "e3" ")"); do
{ MOZ_CrashSequence(__null, 190); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
191 Report(e3);
192
193 // Analyze 1: freed, irrelevant.
194 // Analyze 2: freed, irrelevant.
195 char* f1 = (char*)malloc(64);
196 UseItOrLoseIt(f1, aSeven);
197 free(f1);
198
199 // Analyze 1: ignored.
200 // Analyze 2: irrelevant.
201 Report((void*)(intptr_t)0x0);
202
203 // Analyze 1: mixture of reported and unreported.
204 // Analyze 2: all unreported.
205 Foo(aSeven);
206
207 // Analyze 1: twice-reported.
208 // Analyze 2: twice-reported.
209 char* g1 = (char*)malloc(77);
210 ReportOnAlloc(g1);
211 ReportOnAlloc(g1);
212
213 // Analyze 1: mixture of reported and unreported.
214 // Analyze 2: all unreported.
215 // Nb: this Foo() call is deliberately not adjacent to the previous one. See
216 // the comment about adjacent calls in Foo() for more details.
217 Foo(aSeven);
218
219 // Analyze 1: twice-reported.
220 // Analyze 2: once-reported.
221 char* g2 = (char*)malloc(78);
222 Report(g2);
223 ReportOnAlloc(g2);
224
225 // Analyze 1: twice-reported.
226 // Analyze 2: once-reported.
227 char* g3 = (char*)malloc(79);
228 ReportOnAlloc(g3);
229 Report(g3);
230
231 // All the odd-ball ones.
232 // Analyze 1: all unreported.
233 // Analyze 2: all freed, irrelevant.
234 // XXX: no memalign on Mac
235 // void* w = memalign(64, 65); // rounds up to 128
236 // UseItOrLoseIt(w, aSeven);
237
238 // XXX: posix_memalign doesn't work on B2G
239 // void* x;
240 // posix_memalign(&y, 128, 129); // rounds up to 256
241 // UseItOrLoseIt(x, aSeven);
242
243 // XXX: valloc doesn't work on Windows.
244 // void* y = valloc(1); // rounds up to 4096
245 // UseItOrLoseIt(y, aSeven);
246
247 // XXX: C11 only
248 // void* z = aligned_alloc(64, 256);
249 // UseItOrLoseIt(z, aSeven);
250
251 if (aNum == 1) {
252 // Analyze 1.
253 Analyze(std::move(f));
254 }
255
256 ClearReports();
257
258 //---------
259
260 Report(a2);
261 Report(a2);
262 free(c);
263 free(e);
264 Report(e2);
265 free(e3);
266 // free(w);
267 // free(x);
268 // free(y);
269 // free(z);
270
271 // Do some allocations that will only show up in cumulative mode.
272 for (int i = 0; i < 100; i++) {
273 void* v = malloc(128);
274 UseItOrLoseIt(v, aSeven);
275 free(v);
276 }
277
278 if (aNum == 2) {
279 // Analyze 2.
280 Analyze(std::move(f));
281 }
282}
283
284void TestPartial(const char* aTestName, const char* aMode, int aSeven) {
285 char filename[128];
286 SprintfLiteral(filename, "complete-%s-%s.json", aTestName, aMode);
287 auto f = MakeUnique<FpWriteFunc>(filename);
288
289 char options[128];
290 SprintfLiteral(options, "--mode=%s", aMode);
291 ResetEverything(options);
292
293 int kTenThousand = aSeven + 9993;
294 char* s;
295
296 // The output of this function is deterministic but it relies on the
297 // probability and seeds given to the FastBernoulliTrial instance in
298 // ResetBernoulli(). If they change, the output will change too.
299
300 // Expected fraction with stacks: (1 - (1 - 0.003) ** 16) = 0.0469.
301 // So we expect about 0.0469 * 10000 == 469.
302 // We actually get 511.
303 for (int i = 0; i < kTenThousand; i++) {
304 s = (char*)malloc(16);
305 UseItOrLoseIt(s, aSeven);
306 }
307
308 // Expected fraction with stacks: (1 - (1 - 0.003) ** 128) = 0.3193.
309 // So we expect about 0.3193 * 10000 == 3193.
310 // We actually get 3136.
311 for (int i = 0; i < kTenThousand; i++) {
312 s = (char*)malloc(128);
313 UseItOrLoseIt(s, aSeven);
314 }
315
316 // Expected fraction with stacks: (1 - (1 - 0.003) ** 1024) = 0.9539.
317 // So we expect about 0.9539 * 10000 == 9539.
318 // We actually get 9531.
319 for (int i = 0; i < kTenThousand; i++) {
320 s = (char*)malloc(1024);
321 UseItOrLoseIt(s, aSeven);
322 }
323
324 Analyze(std::move(f));
325}
326
327void TestScan(int aSeven) {
328 auto f = MakeUnique<FpWriteFunc>("basic-scan.json");
329
330 ResetEverything("--mode=scan");
331
332 uintptr_t* p = (uintptr_t*)malloc(6 * sizeof(uintptr_t));
333 UseItOrLoseIt(p, aSeven);
334
335 // Hard-coded values checked by scan-test.py
336 p[0] = 0x123; // outside a block, small value
337 p[1] = 0x0; // null
338 p[2] = (uintptr_t)((uint8_t*)p - 1); // pointer outside a block, but nearby
339 p[3] = (uintptr_t)p; // pointer to start of a block
340 p[4] = (uintptr_t)((uint8_t*)p + 1); // pointer into a block
341 p[5] = 0x0; // trailing null
342
343 Analyze(std::move(f));
344}
345
346void RunTests() {
347 // This test relies on the compiler not doing various optimizations, such as
348 // eliding unused malloc() calls or unrolling loops with fixed iteration
349 // counts. So we compile it with -O0 (or equivalent), which probably prevents
350 // that. We also use the following variable for various loop iteration
351 // counts, just in case compilers might unroll very small loops even with
352 // -O0.
353 int seven = 7;
354
355 // Make sure that DMD is actually running; it is initialized on the first
356 // allocation.
357 int* x = (int*)malloc(100);
358 UseItOrLoseIt(x, seven);
359 MOZ_RELEASE_ASSERT(IsRunning())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsRunning())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsRunning()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("IsRunning()", "/root/firefox-clang/memory/replace/dmd/test/SmokeDMD.cpp"
, 359); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "IsRunning()"
")"); do { MOZ_CrashSequence(__null, 359); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
360
361 // Please keep this in sync with run_test in test_dmd.js.
362
363 TestEmpty("empty", "live");
364 TestEmpty("empty", "dark-matter");
365 TestEmpty("empty", "cumulative");
366
367 TestFull("full", 1, "live", seven);
368 TestFull("full", 1, "dark-matter", seven);
369
370 TestFull("full", 2, "dark-matter", seven);
371 TestFull("full", 2, "cumulative", seven);
372
373 TestPartial("partial", "live", seven);
374
375 TestScan(seven);
376}
377
378int main(int argc, char** argv) {
379 if (argc > 1) {
380 sOutputDir = argv[1];
381 }
382
383 RunTests();
384
385 return 0;
386}