clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name readstrings.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=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/toolkit/mozapps/update/common -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/toolkit/mozapps/update/common -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D NS_NO_XPCOM -D MOZ_APP_BASENAME="Firefox" -I /var/lib/jenkins/workspace/firefox-scan-build/toolkit/mozapps/update/common -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/toolkit/mozapps/update/common -I /var/lib/jenkins/workspace/firefox-scan-build/other-licenses/nsis/Contrib/CityHash/cityhash -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -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-deprecated-this-capture -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 -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-02-011441-20010-1 -x c++ /var/lib/jenkins/workspace/firefox-scan-build/toolkit/mozapps/update/common/readstrings.cpp
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | #include <algorithm> |
8 | #include <iterator> |
9 | #include <string.h> |
10 | #include <stdio.h> |
11 | #include "readstrings.h" |
12 | #include "updatererrors.h" |
13 | |
14 | #ifdef XP_WIN |
15 | # define NS_tfopen _wfopen |
16 | # define OPEN_MODE L"rb" |
17 | # define NS_tstrlen wcslen |
18 | # define NS_tstrcpy wcscpy |
19 | #else |
20 | # define NS_tfopen fopen |
21 | # define OPEN_MODE "r" |
22 | # define NS_tstrlen strlen |
23 | # define NS_tstrcpy strcpy |
24 | #endif |
25 | |
26 | |
27 | class AutoFILE { |
28 | public: |
29 | explicit AutoFILE(FILE* fp) : fp_(fp) {} |
30 | ~AutoFILE() { |
31 | if (fp_) { |
32 | fclose(fp_); |
33 | } |
34 | } |
35 | operator FILE*() { return fp_; } |
36 | |
37 | private: |
38 | FILE* fp_; |
39 | }; |
40 | |
41 | static const char kNL[] = "\r\n"; |
42 | static const char kEquals[] = "="; |
43 | static const char kWhitespace[] = " \t"; |
44 | static const char kRBracket[] = "]"; |
45 | |
46 | static const char* NS_strspnp(const char* delims, const char* str) { |
47 | const char* d; |
48 | do { |
49 | for (d = delims; *d != '\0'; ++d) { |
50 | if (*str == *d) { |
51 | ++str; |
52 | break; |
53 | } |
54 | } |
55 | } while (*d); |
56 | |
57 | return str; |
58 | } |
59 | |
60 | static char* NS_strtok(const char* delims, char** str) { |
61 | if (!*str) { |
62 | return nullptr; |
63 | } |
64 | |
65 | char* ret = (char*)NS_strspnp(delims, *str); |
66 | |
67 | if (!*ret) { |
68 | *str = ret; |
69 | return nullptr; |
70 | } |
71 | |
72 | char* i = ret; |
73 | do { |
74 | for (const char* d = delims; *d != '\0'; ++d) { |
75 | if (*i == *d) { |
76 | *i = '\0'; |
77 | *str = ++i; |
78 | return ret; |
79 | } |
80 | } |
81 | ++i; |
82 | } while (*i); |
83 | |
84 | *str = nullptr; |
85 | return ret; |
86 | } |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | static int find_key(const char* keyList, char* key) { |
94 | if (!keyList) { |
95 | return -1; |
96 | } |
97 | |
98 | int index = 0; |
99 | const char* p = keyList; |
100 | while (*p) { |
101 | if (strcmp(key, p) == 0) { |
102 | return index; |
103 | } |
104 | |
105 | p += strlen(p) + 1; |
106 | index++; |
107 | } |
108 | |
109 | |
110 | return -1; |
111 | } |
112 | |
113 | |
114 | |
115 | |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | |
122 | |
123 | |
124 | |
125 | |
126 | int ReadStrings(const NS_tchar* path, const char* keyList, |
127 | unsigned int numStrings, mozilla::UniquePtr<char[]>* results, |
128 | const char* section) { |
129 | AutoFILE fp(NS_tfopen(path, OPEN_MODE)); |
130 | |
131 | if (!fp) { |
| 2 | | Assuming the condition is false | |
|
| |
132 | return READ_ERROR; |
133 | } |
134 | |
135 | |
136 | if (fseek(fp, 0, SEEK_END) != 0) { |
| 4 | | Assuming the condition is false | |
|
| |
137 | return READ_ERROR; |
138 | } |
139 | |
140 | long len = ftell(fp); |
141 | if (len <= 0) { |
| |
| |
142 | return READ_ERROR; |
143 | } |
144 | |
145 | size_t flen = size_t(len); |
146 | |
147 | char* fileContents = new char[flen + 1]; |
| |
148 | if (!fileContents) { |
| |
149 | return READ_STRINGS_MEM_ERROR; |
150 | } |
151 | |
152 | |
153 | if (fseek(fp, 0, SEEK_SET) != 0) { |
| 10 | | Assuming the condition is true | |
|
| |
154 | return READ_ERROR; |
| 12 | | Potential leak of memory pointed to by 'fileContents' |
|
155 | } |
156 | |
157 | size_t rd = fread(fileContents, sizeof(char), flen, fp); |
158 | if (rd != flen) { |
159 | return READ_ERROR; |
160 | } |
161 | |
162 | fileContents[flen] = '\0'; |
163 | |
164 | int result = ReadStringsFromBuffer(fileContents, keyList, numStrings, results, |
165 | section); |
166 | delete[] fileContents; |
167 | return result; |
168 | } |
169 | |
170 | |
171 | |
172 | int ReadStrings(const NS_tchar* path, StringTable* results) { |
173 | const unsigned int kNumStrings = 2; |
174 | const char* kUpdaterKeys = "Title\0Info\0"; |
175 | mozilla::UniquePtr<char[]> updater_strings[kNumStrings]; |
176 | |
177 | int result = ReadStrings(path, kUpdaterKeys, kNumStrings, updater_strings); |
| |
178 | |
179 | if (result == OK) { |
180 | results->title.swap(updater_strings[0]); |
181 | results->info.swap(updater_strings[1]); |
182 | } |
183 | |
184 | return result; |
185 | } |
186 | |
187 | |
188 | |
189 | |
190 | |
191 | |
192 | |
193 | |
194 | |
195 | |
196 | |
197 | |
198 | |
199 | |
200 | |
201 | |
202 | int ReadStringsFromBuffer(char* stringBuffer, const char* keyList, |
203 | unsigned int numStrings, |
204 | mozilla::UniquePtr<char[]>* results, |
205 | const char* section) { |
206 | bool inStringsSection = false; |
207 | |
208 | unsigned int read = 0; |
209 | |
210 | while (char* token = NS_strtok(kNL, &stringBuffer)) { |
211 | if (token[0] == '#' || token[0] == ';') { |
212 | continue; |
213 | } |
214 | |
215 | token = (char*)NS_strspnp(kWhitespace, token); |
216 | if (!*token) { |
217 | continue; |
218 | } |
219 | |
220 | if (token[0] == '[') { |
221 | ++token; |
222 | char const* currSection = token; |
223 | |
224 | char* rb = NS_strtok(kRBracket, &token); |
225 | if (!rb || NS_strtok(kWhitespace, &token)) { |
226 | |
227 | |
228 | |
229 | |
230 | inStringsSection = false; |
231 | } else { |
232 | if (section) { |
233 | inStringsSection = strcmp(currSection, section) == 0; |
234 | } else { |
235 | inStringsSection = strcmp(currSection, "Strings") == 0; |
236 | } |
237 | } |
238 | |
239 | continue; |
240 | } |
241 | |
242 | if (!inStringsSection) { |
243 | |
244 | |
245 | |
246 | continue; |
247 | } |
248 | |
249 | char* key = token; |
250 | char* e = NS_strtok(kEquals, &token); |
251 | if (!e) { |
252 | continue; |
253 | } |
254 | |
255 | int keyIndex = find_key(keyList, key); |
256 | if (keyIndex >= 0 && (unsigned int)keyIndex < numStrings) { |
257 | size_t valueSize = strlen(token) + 1; |
258 | results[keyIndex] = mozilla::MakeUnique<char[]>(valueSize); |
259 | |
260 | strcpy(results[keyIndex].get(), token); |
261 | read++; |
262 | } |
263 | } |
264 | |
265 | return (read == numStrings) ? OK : PARSE_ERROR; |
266 | } |
267 | |
268 | IniReader::IniReader(const NS_tchar* iniPath, |
269 | const char* section ) { |
270 | if (iniPath) { |
271 | mPath = mozilla::MakeUnique<NS_tchar[]>(NS_tstrlen(iniPath) + 1); |
272 | NS_tstrcpy(mPath.get(), iniPath); |
273 | mMaybeStatusCode = mozilla::Nothing(); |
274 | } else { |
275 | mMaybeStatusCode = mozilla::Some(READ_STRINGS_MEM_ERROR); |
276 | } |
277 | if (section) { |
278 | mSection = mozilla::MakeUnique<char[]>(strlen(section) + 1); |
279 | strcpy(mSection.get(), section); |
280 | } else { |
281 | mSection.reset(nullptr); |
282 | } |
283 | } |
284 | |
285 | bool IniReader::MaybeAddKey(const char* key, size_t& insertionIndex) { |
286 | if (!key || strlen(key) == 0 || mMaybeStatusCode.isSome()) { |
287 | return false; |
288 | } |
289 | auto existingKey = std::find_if(mKeys.begin(), mKeys.end(), |
290 | [=](mozilla::UniquePtr<char[]>& searchKey) { |
291 | return strcmp(key, searchKey.get()) == 0; |
292 | }); |
293 | if (existingKey != mKeys.end()) { |
294 | |
295 | insertionIndex = std::distance(mKeys.begin(), existingKey); |
296 | return true; |
297 | } |
298 | |
299 | |
300 | insertionIndex = mKeys.size(); |
301 | mKeys.emplace_back(mozilla::MakeUnique<char[]>(strlen(key) + 1)); |
302 | strcpy(mKeys.back().get(), key); |
303 | return true; |
304 | } |
305 | |
306 | void IniReader::AddKey(const char* key, mozilla::UniquePtr<char[]>* outputPtr) { |
307 | size_t insertionIndex; |
308 | if (!MaybeAddKey(key, insertionIndex)) { |
309 | return; |
310 | } |
311 | |
312 | if (!outputPtr) { |
313 | return; |
314 | } |
315 | |
316 | mNarrowOutputs.emplace_back(); |
317 | mNarrowOutputs.back().keyIndex = insertionIndex; |
318 | mNarrowOutputs.back().outputPtr = outputPtr; |
319 | } |
320 | |
321 | #ifdef XP_WIN |
322 | void IniReader::AddKey(const char* key, |
323 | mozilla::UniquePtr<wchar_t[]>* outputPtr) { |
324 | size_t insertionIndex; |
325 | if (!MaybeAddKey(key, insertionIndex)) { |
326 | return; |
327 | } |
328 | |
329 | if (!outputPtr) { |
330 | return; |
331 | } |
332 | |
333 | mWideOutputs.emplace_back(); |
334 | mWideOutputs.back().keyIndex = insertionIndex; |
335 | mWideOutputs.back().outputPtr = outputPtr; |
336 | } |
337 | |
338 | |
339 | static bool ConvertToWide(const char* toConvert, |
340 | mozilla::UniquePtr<wchar_t[]>* result) { |
341 | int bufferSize = MultiByteToWideChar(CP_UTF8, 0, toConvert, -1, nullptr, 0); |
342 | *result = mozilla::MakeUnique<wchar_t[]>(bufferSize); |
343 | int charsWritten = |
344 | MultiByteToWideChar(CP_UTF8, 0, toConvert, -1, result->get(), bufferSize); |
345 | return charsWritten > 0; |
346 | } |
347 | #endif |
348 | |
349 | int IniReader::Read() { |
350 | if (mMaybeStatusCode.isSome()) { |
351 | return mMaybeStatusCode.value(); |
352 | } |
353 | |
354 | if (mKeys.empty()) { |
355 | |
356 | mMaybeStatusCode = mozilla::Some(OK); |
357 | return OK; |
358 | } |
359 | |
360 | |
361 | |
362 | size_t keyListSize = 1; |
363 | for (const auto& key : mKeys) { |
364 | keyListSize += strlen(key.get()); |
365 | keyListSize += 1; |
366 | } |
367 | mozilla::UniquePtr<char[]> keyList = mozilla::MakeUnique<char[]>(keyListSize); |
368 | char* keyListPtr = keyList.get(); |
369 | for (const auto& key : mKeys) { |
370 | strcpy(keyListPtr, key.get()); |
371 | |
372 | keyListPtr += strlen(key.get()) + 1; |
373 | } |
374 | *keyListPtr = '\0'; |
375 | |
376 | |
377 | mozilla::UniquePtr<mozilla::UniquePtr<char[]>[]> results = |
378 | mozilla::MakeUnique<mozilla::UniquePtr<char[]>[]>(mKeys.size()); |
379 | |
380 | |
381 | int statusCode = ReadStrings(mPath.get(), keyList.get(), mKeys.size(), |
382 | results.get(), mSection.get()); |
383 | mMaybeStatusCode = mozilla::Some(statusCode); |
384 | |
385 | if (statusCode != OK) { |
386 | return statusCode; |
387 | } |
388 | |
389 | |
390 | for (const auto output : mNarrowOutputs) { |
391 | char* valueBuffer = results[output.keyIndex].get(); |
392 | if (valueBuffer) { |
393 | *(output.outputPtr) = |
394 | mozilla::MakeUnique<char[]>(strlen(valueBuffer) + 1); |
395 | strcpy(output.outputPtr->get(), valueBuffer); |
396 | } |
397 | } |
398 | |
399 | #ifdef XP_WIN |
400 | for (const auto output : mWideOutputs) { |
401 | char* valueBuffer = results[output.keyIndex].get(); |
402 | if (valueBuffer) { |
403 | if (!ConvertToWide(valueBuffer, output.outputPtr)) { |
404 | statusCode = STRING_CONVERSION_ERROR; |
405 | } |
406 | } |
407 | } |
408 | #endif |
409 | |
410 | return statusCode; |
411 | } |