Bug Summary

File:src/redis-check-rdb.c
Warning:line 287, column 18
Although the value stored to 'when' is used in the enclosing expression, the value is never actually read from 'when'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name redis-check-rdb.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -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 -mrelocation-model static -mthread-model posix -mframe-pointer=none -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-10/lib/clang/10.0.0 -D REDIS_STATIC= -I ../deps/hiredis -I ../deps/linenoise -I ../deps/lua/src -I ../deps/hdr_histogram -D USE_JEMALLOC -I ../deps/jemalloc/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-10/lib/clang/10.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-c11-extensions -Wno-missing-field-initializers -std=c11 -fdebug-compilation-dir /home/netto/Desktop/redis-6.2.1/src -ferror-limit 19 -fmessage-length 0 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-03-14-133648-8817-1 -x c redis-check-rdb.c
1/*
2 * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "mt19937-64.h"
31#include "server.h"
32#include "rdb.h"
33
34#include <stdarg.h>
35#include <sys/time.h>
36#include <unistd.h>
37
38void createSharedObjects(void);
39void rdbLoadProgressCallback(rio *r, const void *buf, size_t len);
40int rdbCheckMode = 0;
41
42struct {
43 rio *rio;
44 robj *key; /* Current key we are reading. */
45 int key_type; /* Current key type if != -1. */
46 unsigned long keys; /* Number of keys processed. */
47 unsigned long expires; /* Number of keys with an expire. */
48 unsigned long already_expired; /* Number of keys already expired. */
49 int doing; /* The state while reading the RDB. */
50 int error_set; /* True if error is populated. */
51 char error[1024];
52} rdbstate;
53
54/* At every loading step try to remember what we were about to do, so that
55 * we can log this information when an error is encountered. */
56#define RDB_CHECK_DOING_START0 0
57#define RDB_CHECK_DOING_READ_TYPE1 1
58#define RDB_CHECK_DOING_READ_EXPIRE2 2
59#define RDB_CHECK_DOING_READ_KEY3 3
60#define RDB_CHECK_DOING_READ_OBJECT_VALUE4 4
61#define RDB_CHECK_DOING_CHECK_SUM5 5
62#define RDB_CHECK_DOING_READ_LEN6 6
63#define RDB_CHECK_DOING_READ_AUX7 7
64#define RDB_CHECK_DOING_READ_MODULE_AUX8 8
65
66char *rdb_check_doing_string[] = {
67 "start",
68 "read-type",
69 "read-expire",
70 "read-key",
71 "read-object-value",
72 "check-sum",
73 "read-len",
74 "read-aux",
75 "read-module-aux"
76};
77
78char *rdb_type_string[] = {
79 "string",
80 "list-linked",
81 "set-hashtable",
82 "zset-v1",
83 "hash-hashtable",
84 "zset-v2",
85 "module-value",
86 "","",
87 "hash-zipmap",
88 "list-ziplist",
89 "set-intset",
90 "zset-ziplist",
91 "hash-ziplist",
92 "quicklist",
93 "stream"
94};
95
96/* Show a few stats collected into 'rdbstate' */
97void rdbShowGenericInfo(void) {
98 printf("[info] %lu keys read\n", rdbstate.keys);
99 printf("[info] %lu expires\n", rdbstate.expires);
100 printf("[info] %lu already expired\n", rdbstate.already_expired);
101}
102
103/* Called on RDB errors. Provides details about the RDB and the offset
104 * we were when the error was detected. */
105void rdbCheckError(const char *fmt, ...) {
106 char msg[1024];
107 va_list ap;
108
109 va_start(ap, fmt)__builtin_va_start(ap, fmt);
110 vsnprintf(msg, sizeof(msg), fmt, ap);
111 va_end(ap)__builtin_va_end(ap);
112
113 printf("--- RDB ERROR DETECTED ---\n");
114 printf("[offset %llu] %s\n",
115 (unsigned long long) (rdbstate.rio ?
116 rdbstate.rio->processed_bytes : 0), msg);
117 printf("[additional info] While doing: %s\n",
118 rdb_check_doing_string[rdbstate.doing]);
119 if (rdbstate.key)
120 printf("[additional info] Reading key '%s'\n",
121 (char*)rdbstate.key->ptr);
122 if (rdbstate.key_type != -1)
123 printf("[additional info] Reading type %d (%s)\n",
124 rdbstate.key_type,
125 ((unsigned)rdbstate.key_type <
126 sizeof(rdb_type_string)/sizeof(char*)) ?
127 rdb_type_string[rdbstate.key_type] : "unknown");
128 rdbShowGenericInfo();
129}
130
131/* Print informations during RDB checking. */
132void rdbCheckInfo(const char *fmt, ...) {
133 char msg[1024];
134 va_list ap;
135
136 va_start(ap, fmt)__builtin_va_start(ap, fmt);
137 vsnprintf(msg, sizeof(msg), fmt, ap);
138 va_end(ap)__builtin_va_end(ap);
139
140 printf("[offset %llu] %s\n",
141 (unsigned long long) (rdbstate.rio ?
142 rdbstate.rio->processed_bytes : 0), msg);
143}
144
145/* Used inside rdb.c in order to log specific errors happening inside
146 * the RDB loading internals. */
147void rdbCheckSetError(const char *fmt, ...) {
148 va_list ap;
149
150 va_start(ap, fmt)__builtin_va_start(ap, fmt);
151 vsnprintf(rdbstate.error, sizeof(rdbstate.error), fmt, ap);
152 va_end(ap)__builtin_va_end(ap);
153 rdbstate.error_set = 1;
154}
155
156/* During RDB check we setup a special signal handler for memory violations
157 * and similar conditions, so that we can log the offending part of the RDB
158 * if the crash is due to broken content. */
159void rdbCheckHandleCrash(int sig, siginfo_t *info, void *secret) {
160 UNUSED(sig)((void) sig);
161 UNUSED(info)((void) info);
162 UNUSED(secret)((void) secret);
163
164 rdbCheckError("Server crash checking the specified RDB file!");
165 exit(1);
166}
167
168void rdbCheckSetupSignals(void) {
169 struct sigaction act;
170
171 sigemptyset(&act.sa_mask);
172 act.sa_flags = SA_NODEFER0x40000000 | SA_RESETHAND0x80000000 | SA_SIGINFO4;
173 act.sa_sigaction__sigaction_handler.sa_sigaction = rdbCheckHandleCrash;
174 sigaction(SIGSEGV11, &act, NULL((void*)0));
175 sigaction(SIGBUS7, &act, NULL((void*)0));
176 sigaction(SIGFPE8, &act, NULL((void*)0));
177 sigaction(SIGILL4, &act, NULL((void*)0));
178 sigaction(SIGABRT6, &act, NULL((void*)0));
179}
180
181/* Check the specified RDB file. Return 0 if the RDB looks sane, otherwise
182 * 1 is returned.
183 * The file is specified as a filename in 'rdbfilename' if 'fp' is not NULL,
184 * otherwise the already open file 'fp' is checked. */
185int redis_check_rdb(char *rdbfilename, FILE *fp) {
186 uint64_t dbid;
187 int type, rdbver;
188 char buf[1024];
189 long long expiretime, now = mstime();
190 static rio rdb; /* Pointed by global struct riostate. */
191
192 int closefile = (fp == NULL((void*)0));
193 if (fp == NULL((void*)0) && (fp = fopen(rdbfilename,"r")) == NULL((void*)0)) return 1;
194
195 rioInitWithFile(&rdb,fp);
196 rdbstate.rio = &rdb;
197 rdb.update_cksum = rdbLoadProgressCallback;
198 if (rioRead(&rdb,buf,9) == 0) goto eoferr;
199 buf[9] = '\0';
200 if (memcmp(buf,"REDIS",5) != 0) {
201 rdbCheckError("Wrong signature trying to load DB from file");
202 goto err;
203 }
204 rdbver = atoi(buf+5);
205 if (rdbver < 1 || rdbver > RDB_VERSION9) {
206 rdbCheckError("Can't handle RDB format version %d",rdbver);
207 goto err;
208 }
209
210 expiretime = -1;
211 startLoadingFile(fp, rdbfilename, RDBFLAGS_NONE0);
212 while(1) {
213 robj *key, *val;
214
215 /* Read type. */
216 rdbstate.doing = RDB_CHECK_DOING_READ_TYPE1;
217 if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
218
219 /* Handle special types. */
220 if (type == RDB_OPCODE_EXPIRETIME253) {
221 rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE2;
222 /* EXPIRETIME: load an expire associated with the next key
223 * to load. Note that after loading an expire we need to
224 * load the actual type, and continue. */
225 expiretime = rdbLoadTime(&rdb);
226 expiretime *= 1000;
227 if (rioGetReadError(&rdb)) goto eoferr;
228 continue; /* Read next opcode. */
229 } else if (type == RDB_OPCODE_EXPIRETIME_MS252) {
230 /* EXPIRETIME_MS: milliseconds precision expire times introduced
231 * with RDB v3. Like EXPIRETIME but no with more precision. */
232 rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE2;
233 expiretime = rdbLoadMillisecondTime(&rdb, rdbver);
234 if (rioGetReadError(&rdb)) goto eoferr;
235 continue; /* Read next opcode. */
236 } else if (type == RDB_OPCODE_FREQ249) {
237 /* FREQ: LFU frequency. */
238 uint8_t byte;
239 if (rioRead(&rdb,&byte,1) == 0) goto eoferr;
240 continue; /* Read next opcode. */
241 } else if (type == RDB_OPCODE_IDLE248) {
242 /* IDLE: LRU idle time. */
243 if (rdbLoadLen(&rdb,NULL((void*)0)) == RDB_LENERR(18446744073709551615UL)) goto eoferr;
244 continue; /* Read next opcode. */
245 } else if (type == RDB_OPCODE_EOF255) {
246 /* EOF: End of file, exit the main loop. */
247 break;
248 } else if (type == RDB_OPCODE_SELECTDB254) {
249 /* SELECTDB: Select the specified database. */
250 rdbstate.doing = RDB_CHECK_DOING_READ_LEN6;
251 if ((dbid = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL))
252 goto eoferr;
253 rdbCheckInfo("Selecting DB ID %d", dbid);
254 continue; /* Read type again. */
255 } else if (type == RDB_OPCODE_RESIZEDB251) {
256 /* RESIZEDB: Hint about the size of the keys in the currently
257 * selected data base, in order to avoid useless rehashing. */
258 uint64_t db_size, expires_size;
259 rdbstate.doing = RDB_CHECK_DOING_READ_LEN6;
260 if ((db_size = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL))
261 goto eoferr;
262 if ((expires_size = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL))
263 goto eoferr;
264 continue; /* Read type again. */
265 } else if (type == RDB_OPCODE_AUX250) {
266 /* AUX: generic string-string fields. Use to add state to RDB
267 * which is backward compatible. Implementations of RDB loading
268 * are requierd to skip AUX fields they don't understand.
269 *
270 * An AUX field is composed of two strings: key and value. */
271 robj *auxkey, *auxval;
272 rdbstate.doing = RDB_CHECK_DOING_READ_AUX7;
273 if ((auxkey = rdbLoadStringObject(&rdb)) == NULL((void*)0)) goto eoferr;
274 if ((auxval = rdbLoadStringObject(&rdb)) == NULL((void*)0)) goto eoferr;
275
276 rdbCheckInfo("AUX FIELD %s = '%s'",
277 (char*)auxkey->ptr, (char*)auxval->ptr);
278 decrRefCount(auxkey);
279 decrRefCount(auxval);
280 continue; /* Read type again. */
281 } else if (type == RDB_OPCODE_MODULE_AUX247) {
282 /* AUX: Auxiliary data for modules. */
283 uint64_t moduleid, when_opcode, when;
284 rdbstate.doing = RDB_CHECK_DOING_READ_MODULE_AUX8;
285 if ((moduleid = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL)) goto eoferr;
286 if ((when_opcode = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL)) goto eoferr;
287 if ((when = rdbLoadLen(&rdb,NULL((void*)0))) == RDB_LENERR(18446744073709551615UL)) goto eoferr;
Although the value stored to 'when' is used in the enclosing expression, the value is never actually read from 'when'
288
289 char name[10];
290 moduleTypeNameByID(name,moduleid);
291 rdbCheckInfo("MODULE AUX for: %s", name);
292
293 robj *o = rdbLoadCheckModuleValue(&rdb,name);
294 decrRefCount(o);
295 continue; /* Read type again. */
296 } else {
297 if (!rdbIsObjectType(type)((type >= 0 && type <= 7) || (type >= 9 &&
type <= 15))
) {
298 rdbCheckError("Invalid object type: %d", type);
299 goto err;
300 }
301 rdbstate.key_type = type;
302 }
303
304 /* Read key */
305 rdbstate.doing = RDB_CHECK_DOING_READ_KEY3;
306 if ((key = rdbLoadStringObject(&rdb)) == NULL((void*)0)) goto eoferr;
307 rdbstate.key = key;
308 rdbstate.keys++;
309 /* Read value */
310 rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE4;
311 if ((val = rdbLoadObject(type,&rdb,key->ptr)) == NULL((void*)0)) goto eoferr;
312 /* Check if the key already expired. */
313 if (expiretime != -1 && expiretime < now)
314 rdbstate.already_expired++;
315 if (expiretime != -1) rdbstate.expires++;
316 rdbstate.key = NULL((void*)0);
317 decrRefCount(key);
318 decrRefCount(val);
319 rdbstate.key_type = -1;
320 expiretime = -1;
321 }
322 /* Verify the checksum if RDB version is >= 5 */
323 if (rdbver >= 5 && server.rdb_checksum) {
324 uint64_t cksum, expected = rdb.cksum;
325
326 rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM5;
327 if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
328 memrev64ifbe(&cksum)((void)(0));
329 if (cksum == 0) {
330 rdbCheckInfo("RDB file was saved with checksum disabled: no check performed.");
331 } else if (cksum != expected) {
332 rdbCheckError("RDB CRC error");
333 goto err;
334 } else {
335 rdbCheckInfo("Checksum OK");
336 }
337 }
338
339 if (closefile) fclose(fp);
340 stopLoading(1);
341 return 0;
342
343eoferr: /* unexpected end of file is handled here with a fatal exit */
344 if (rdbstate.error_set) {
345 rdbCheckError(rdbstate.error);
346 } else {
347 rdbCheckError("Unexpected EOF reading RDB file");
348 }
349err:
350 if (closefile) fclose(fp);
351 stopLoading(0);
352 return 1;
353}
354
355/* RDB check main: called form server.c when Redis is executed with the
356 * redis-check-rdb alias, on during RDB loading errors.
357 *
358 * The function works in two ways: can be called with argc/argv as a
359 * standalone executable, or called with a non NULL 'fp' argument if we
360 * already have an open file to check. This happens when the function
361 * is used to check an RDB preamble inside an AOF file.
362 *
363 * When called with fp = NULL, the function never returns, but exits with the
364 * status code according to success (RDB is sane) or error (RDB is corrupted).
365 * Otherwise if called with a non NULL fp, the function returns C_OK or
366 * C_ERR depending on the success or failure. */
367int redis_check_rdb_main(int argc, char **argv, FILE *fp) {
368 struct timeval tv;
369
370 if (argc != 2 && fp == NULL((void*)0)) {
371 fprintf(stderrstderr, "Usage: %s <rdb-file-name>\n", argv[0]);
372 exit(1);
373 }
374
375 gettimeofday(&tv, NULL((void*)0));
376 init_genrand64(((long long) tv.tv_sec * 1000000 + tv.tv_usec) ^ getpid());
377
378 /* In order to call the loading functions we need to create the shared
379 * integer objects, however since this function may be called from
380 * an already initialized Redis instance, check if we really need to. */
381 if (shared.integers[0] == NULL((void*)0))
382 createSharedObjects();
383 server.loading_process_events_interval_bytes = 0;
384 server.sanitize_dump_payload = SANITIZE_DUMP_YES1;
385 rdbCheckMode = 1;
386 rdbCheckInfo("Checking RDB file %s", argv[1]);
387 rdbCheckSetupSignals();
388 int retval = redis_check_rdb(argv[1],fp);
389 if (retval == 0) {
390 rdbCheckInfo("\\o/ RDB looks OK! \\o/");
391 rdbShowGenericInfo();
392 }
393 if (fp) return (retval == 0) ? C_OK0 : C_ERR-1;
394 exit(retval);
395}