* coverity problems in reftable code
@ 2021-12-04 2:13 Jeff King
[not found] ` <CAFQ2z_OK5949p1WfovJ00Katk5hTv_oeLo-ZRCi1XqrtzQqL2g@mail.gmail.com>
0 siblings, 1 reply; 7+ messages in thread
From: Jeff King @ 2021-12-04 2:13 UTC (permalink / raw)
To: Han-Wen Nienhuys; +Cc: git
Hi Han-Wen,
We're not doing project-wide analysis with Coverity right now, but I've
been doing builds of my personal repo, which I usually build off of
next. And since hn/reftable just hit next, it got included in my latest
build.
It came up with several complaints. Some of them are dumb and can be
ignored (e.g., using rand() in a test harness, oh no!) but I poked at a
few and they look like real issues:
- the stack overflow in reftable_log_record_print() is real; I think you
want HEXSZ instead of RAWSZ for your buffer (also, should it be
GIT_MAX_HEXSZ?)
- various fd/memory leaks on error returns
- A lot of your structs have vtables. Initializing them to NULL, as in
reftable_reader_refs_for_indexed(), leaves the risk that we'll try
to call a NULL function pointer, even if it's for something simple
like cleaning up. That's the case here (if loading want_rec fails,
we'll jump to "done" and try to clean up got_rec, even though it's
still got a NULL vtable). One solution is for reftable_record_release()
to check for NULL (and other destructors; there's a similar case in
the last snippet below for reftable_reader_free()).
But I also wasn't sure how extensive the use of polymorphism was,
and if it might be possible for us to do more at initialization
time. Something like:
diff --git a/reftable/reader.c b/reftable/reader.c
index 2db0019111..8ac248b070 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -653,21 +653,19 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
.hash_prefix = oid,
.hash_prefix_len = r->object_id_len,
};
- struct reftable_record want_rec = { NULL };
+ struct reftable_record want_rec = REFTABLE_RECORD_OBJ(&want);
struct reftable_iterator oit = { NULL };
struct reftable_obj_record got = { NULL };
- struct reftable_record got_rec = { NULL };
+ struct reftable_record got_rec = REFTABLE_RECORD_OBJ(&got);
int err = 0;
struct indexed_table_ref_iter *itr = NULL;
/* Look through the reverse index. */
- reftable_record_from_obj(&want_rec, &want);
err = reader_seek(r, &oit, &want_rec);
if (err != 0)
goto done;
/* read out the reftable_obj_record */
- reftable_record_from_obj(&got_rec, &got);
err = iterator_next(&oit, &got_rec);
if (err < 0)
goto done;
diff --git a/reftable/record.c b/reftable/record.c
index 5a4bbed6c8..4c34752d18 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -572,7 +572,7 @@ static int not_a_deletion(const void *UNUSED(p))
return 0;
}
-static struct reftable_record_vtable reftable_obj_record_vtable = {
+struct reftable_record_vtable reftable_obj_record_vtable = {
.key = &reftable_obj_record_key,
.type = BLOCK_TYPE_OBJ,
.copy_from = &reftable_obj_record_copy_from,
@@ -1106,14 +1106,6 @@ void reftable_record_from_ref(struct reftable_record *rec,
rec->ops = &reftable_ref_record_vtable;
}
-void reftable_record_from_obj(struct reftable_record *rec,
- struct reftable_obj_record *obj_rec)
-{
- assert(!rec->ops);
- rec->data = obj_rec;
- rec->ops = &reftable_obj_record_vtable;
-}
-
void reftable_record_from_index(struct reftable_record *rec,
struct reftable_index_record *index_rec)
{
diff --git a/reftable/record.h b/reftable/record.h
index 498e8c50bf..316cde9182 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -66,6 +66,12 @@ struct reftable_record {
struct reftable_record_vtable *ops;
};
+extern struct reftable_record_vtable reftable_obj_record_vtable;
+#define REFTABLE_RECORD_OBJ(obj_record) { \
+ .data = obj_record, \
+ .ops = &reftable_obj_record_vtable \
+}
+
/* returns true for recognized block types. Block start with the block type. */
int reftable_is_block_type(uint8_t typ);
@@ -119,8 +125,6 @@ void reftable_record_destroy(struct reftable_record *rec);
/* initialize generic records from concrete records. The generic record should
* be zeroed out. */
-void reftable_record_from_obj(struct reftable_record *rec,
- struct reftable_obj_record *objrec);
void reftable_record_from_index(struct reftable_record *rec,
struct reftable_index_record *idxrec);
void reftable_record_from_ref(struct reftable_record *rec,
but there may be other cases which really want the more dynamic
initialization. And also, the virtual release function would need to
handle the NULL data as well.
I similarly wondered if these polymorphic types could be using a union
within reftable_record, rather than pointing to a separate stack
variable. Then you could initialize the whole thing without worrying
about intermediate NULLs (and also there's less pointer chasing and it's
a little bit more type safe than a void pointer). But again, I don't
know the code well enough to know if that would cover all of your cases.
The summary of issues is below. You can get more details on their site.
I _think_ I've configured it so that anybody can look at:
https://scan.coverity.com/projects/peff-git/view_defects
but you'll probably need to make a log in (or connect with a GitHub
account). I usually find the summary enough to find issues, but
sometimes it's useful to look at the site because it outlines the full
path of assumptions (not just "here we dereference NULL", but the
sequence of code that goes from knowing that a variable is NULL to the
point of dereferencing it).
I'm happy to help dig into any of them more, or even work on patches.
But per the above diff, I wasn't sure how far to go. :)
-Peff
-- >8 --
** CID 1467043: Resource leaks (RESOURCE_LEAK)
/reftable/block.c: 220 in block_reader_init()
________________________________________________________________________________________________________
*** CID 1467043: Resource leaks (RESOURCE_LEAK)
/reftable/block.c: 220 in block_reader_init()
214 block->data + block_header_skip, &src_len)) {
215 reftable_free(uncompressed);
216 return REFTABLE_ZLIB_ERROR;
217 }
218
219 if (dst_len + block_header_skip != sz)
>>> CID 1467043: Resource leaks (RESOURCE_LEAK)
>>> Variable "uncompressed" going out of scope leaks the storage it points to.
220 return REFTABLE_FORMAT_ERROR;
221
222 /* We're done with the input data. */
223 reftable_block_done(block);
224 block->data = uncompressed;
225 block->len = sz;
** CID 1467042: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467042: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 692 in reftable_reader_refs_for_indexed()
686 goto done;
687 got.offsets = NULL;
688 iterator_from_indexed_table_ref_iter(it, itr);
689
690 done:
691 reftable_iterator_destroy(&oit);
>>> CID 1467042: Null pointer dereferences (FORWARD_NULL)
>>> Passing "&got_rec" to "reftable_record_release", which dereferences null "got_rec.ops".
692 reftable_record_release(&got_rec);
693 return err;
694 }
695
696 static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
697 struct reftable_iterator *it,
** CID 1467041: Resource leaks (RESOURCE_LEAK)
/reftable/blocksource.c: 138 in reftable_block_source_from_file()
________________________________________________________________________________________________________
*** CID 1467041: Resource leaks (RESOURCE_LEAK)
/reftable/blocksource.c: 138 in reftable_block_source_from_file()
132 }
133 return -1;
134 }
135
136 err = fstat(fd, &st);
137 if (err < 0)
>>> CID 1467041: Resource leaks (RESOURCE_LEAK)
>>> Handle variable "fd" going out of scope leaks the handle.
138 return -1;
139
140 p = reftable_calloc(sizeof(struct file_block_source));
141 p->size = st.st_size;
142 p->fd = fd;
143
** CID 1467040: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467040: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 159 in test_reftable_ref_record_roundtrip()
153
154 reftable_record_from_ref(&rec_out, &out);
155 m = reftable_record_decode(&rec_out, key, i, dest,
156 GIT_SHA1_RAWSZ);
157 EXPECT(n == m);
158
>>> CID 1467040: Null pointer dereferences (FORWARD_NULL)
>>> Passing "&out" to "reftable_ref_record_equal", which dereferences null "out.refname".
159 EXPECT(reftable_ref_record_equal(&in, &out, GIT_SHA1_RAWSZ));
160 reftable_record_release(&rec_out);
161
162 strbuf_release(&key);
163 reftable_ref_record_release(&in);
164 }
** CID 1467039: (TAINTED_SCALAR)
/reftable/block.c: 212 in block_reader_init()
________________________________________________________________________________________________________
*** CID 1467039: (TAINTED_SCALAR)
/reftable/block.c: 212 in block_reader_init()
206 uint8_t *uncompressed = reftable_malloc(sz);
207
208 /* Copy over the block header verbatim. It's not compressed. */
209 memcpy(uncompressed, block->data, block_header_skip);
210
211 /* Uncompress */
>>> CID 1467039: (TAINTED_SCALAR)
>>> Passing tainted expression "dst_len" to "uncompress2", which uses it as an offset.
212 if (Z_OK !=
213 uncompress2(uncompressed + block_header_skip, &dst_len,
214 block->data + block_header_skip, &src_len)) {
215 reftable_free(uncompressed);
216 return REFTABLE_ZLIB_ERROR;
217 }
/reftable/block.c: 206 in block_reader_init()
200 int block_header_skip = 4 + header_off;
201 uLongf dst_len = sz - block_header_skip; /* total size of dest
202 buffer. */
203 uLongf src_len = block->len - block_header_skip;
204 /* Log blocks specify the *uncompressed* size in their header.
205 */
>>> CID 1467039: (TAINTED_SCALAR)
>>> Passing tainted expression "sz" to "reftable_malloc", which uses it as an allocation size.
206 uint8_t *uncompressed = reftable_malloc(sz);
207
208 /* Copy over the block header verbatim. It's not compressed. */
209 memcpy(uncompressed, block->data, block_header_skip);
210
211 /* Uncompress */
** CID 1467038: (DC.WEAK_CRYPTO)
/reftable/readwrite_test.c: 145 in test_log_buffer_size()
/reftable/readwrite_test.c: 144 in test_log_buffer_size()
________________________________________________________________________________________________________
*** CID 1467038: (DC.WEAK_CRYPTO)
/reftable/readwrite_test.c: 145 in test_log_buffer_size()
139 /* This tests buffer extension for log compression. Must use a random
140 hash, to ensure that the compressed part is larger than the original.
141 */
142 uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
143 for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
144 hash1[i] = (uint8_t)(rand() % 256);
>>> CID 1467038: (DC.WEAK_CRYPTO)
>>> "rand" should not be used for security-related applications, because linear congruential algorithms are too easy to break.
145 hash2[i] = (uint8_t)(rand() % 256);
146 }
147 log.value.update.old_hash = hash1;
148 log.value.update.new_hash = hash2;
149 reftable_writer_set_limits(w, update_index, update_index);
150 err = reftable_writer_add_log(w, &log);
/reftable/readwrite_test.c: 144 in test_log_buffer_size()
138
139 /* This tests buffer extension for log compression. Must use a random
140 hash, to ensure that the compressed part is larger than the original.
141 */
142 uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
143 for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
>>> CID 1467038: (DC.WEAK_CRYPTO)
>>> "rand" should not be used for security-related applications, because linear congruential algorithms are too easy to break.
144 hash1[i] = (uint8_t)(rand() % 256);
145 hash2[i] = (uint8_t)(rand() % 256);
146 }
147 log.value.update.old_hash = hash1;
148 log.value.update.new_hash = hash2;
149 reftable_writer_set_limits(w, update_index, update_index);
** CID 1467037: Security best practices violations (DC.WEAK_CRYPTO)
/reftable/stack.c: 439 in format_name()
________________________________________________________________________________________________________
*** CID 1467037: Security best practices violations (DC.WEAK_CRYPTO)
/reftable/stack.c: 439 in format_name()
433 return 0;
434 }
435
436 static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
437 {
438 char buf[100];
>>> CID 1467037: Security best practices violations (DC.WEAK_CRYPTO)
>>> "rand" should not be used for security-related applications, because linear congruential algorithms are too easy to break.
439 uint32_t rnd = (uint32_t)rand();
440 snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
441 min, max, rnd);
442 strbuf_reset(dest);
443 strbuf_addstr(dest, buf);
444 }
** CID 1467036: Code maintainability issues (UNUSED_VALUE)
/reftable/stack_test.c: 816 in test_reftable_stack_auto_compaction()
________________________________________________________________________________________________________
*** CID 1467036: Code maintainability issues (UNUSED_VALUE)
/reftable/stack_test.c: 816 in test_reftable_stack_auto_compaction()
810 };
811 snprintf(name, sizeof(name), "branch%04d", i);
812
813 err = reftable_stack_add(st, &write_test_ref, &ref);
814 EXPECT_ERR(err);
815
>>> CID 1467036: Code maintainability issues (UNUSED_VALUE)
>>> Assigning value from "reftable_stack_auto_compact(st)" to "err" here, but that stored value is overwritten before it can be used.
816 err = reftable_stack_auto_compact(st);
817 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
818 }
819
820 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
821 (uint64_t)(N * fastlog2(N)));
** CID 1467035: (OVERRUN)
________________________________________________________________________________________________________
*** CID 1467035: (OVERRUN)
/reftable/record.c: 603 in reftable_log_record_print()
597 printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
598 log->refname, log->update_index, log->value.update.name,
599 log->value.update.email, log->value.update.time,
600 log->value.update.tz_offset);
601 hex_format(hex, log->value.update.old_hash, hash_size(hash_id));
602 printf("%s => ", hex);
>>> CID 1467035: (OVERRUN)
>>> Overrunning array "hex" of 33 bytes by passing it to a function which accesses it at byte offset 63 using argument "hash_size(hash_id)" (which evaluates to 32).
603 hex_format(hex, log->value.update.new_hash, hash_size(hash_id));
604 printf("%s\n\n%s\n}\n", hex, log->value.update.message);
605 break;
606 }
607 }
608
/reftable/record.c: 601 in reftable_log_record_print()
595 break;
596 case REFTABLE_LOG_UPDATE:
597 printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
598 log->refname, log->update_index, log->value.update.name,
599 log->value.update.email, log->value.update.time,
600 log->value.update.tz_offset);
>>> CID 1467035: (OVERRUN)
>>> Overrunning array "hex" of 33 bytes by passing it to a function which accesses it at byte offset 63 using argument "hash_size(hash_id)" (which evaluates to 32).
601 hex_format(hex, log->value.update.old_hash, hash_size(hash_id));
602 printf("%s => ", hex);
603 hex_format(hex, log->value.update.new_hash, hash_size(hash_id));
604 printf("%s\n\n%s\n}\n", hex, log->value.update.message);
605 break;
606 }
** CID 1467034: Error handling issues (CHECKED_RETURN)
/reftable/stack_test.c: 92 in test_read_file()
________________________________________________________________________________________________________
*** CID 1467034: Error handling issues (CHECKED_RETURN)
/reftable/stack_test.c: 92 in test_read_file()
86 EXPECT_ERR(err);
87
88 for (i = 0; names[i]; i++) {
89 EXPECT(0 == strcmp(want[i], names[i]));
90 }
91 free_names(names);
>>> CID 1467034: Error handling issues (CHECKED_RETURN)
>>> Calling "remove(fn)" without checking return value. This library function may fail and return an error code.
92 remove(fn);
93 }
94
95 static void test_parse_names(void)
96 {
97 char buf[] = "line\n";
** CID 1467033: Resource leaks (RESOURCE_LEAK)
/reftable/stack.c: 951 in stack_compact_range()
________________________________________________________________________________________________________
*** CID 1467033: Resource leaks (RESOURCE_LEAK)
/reftable/stack.c: 951 in stack_compact_range()
945 subtable_locks[j] = subtab_lock.buf;
946 delete_on_success[j] = subtab_file_name.buf;
947 j++;
948
949 if (err != 0)
950 goto done;
>>> CID 1467033: Resource leaks (RESOURCE_LEAK)
>>> Handle variable "sublock_file_fd" going out of scope leaks the handle.
951 }
952
953 err = unlink(lock_file_name.buf);
954 if (err < 0)
955 goto done;
956 have_lock = 0;
** CID 1467032: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467032: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 504 in reader_seek_indexed()
498 reftable_record_from_index(&index_result_rec, &index_result);
499
500 err = reader_start(r, &index_iter, reftable_record_type(rec), 1);
501 if (err < 0)
502 goto done;
503
>>> CID 1467032: Null pointer dereferences (FORWARD_NULL)
>>> Passing "&index_iter" to "reader_seek_linear", which dereferences null "index_iter.r".
504 err = reader_seek_linear(&index_iter, &want_index_rec);
505 while (1) {
506 err = table_iter_next(&index_iter, &index_result_rec);
507 table_iter_block_done(&index_iter);
508 if (err != 0)
509 goto done;
** CID 1467031: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467031: Null pointer dereferences (FORWARD_NULL)
/reftable/stack.c: 710 in reftable_addition_add()
704 unlink(temp_tab_file_name.buf);
705 }
706
707 strbuf_release(&temp_tab_file_name);
708 strbuf_release(&tab_file_name);
709 strbuf_release(&next_name);
>>> CID 1467031: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "wr" to "reftable_writer_free", which dereferences it.
710 reftable_writer_free(wr);
711 return err;
712 }
713
714 uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
715 {
** CID 1467030: Security best practices violations (DC.WEAK_CRYPTO)
/reftable/stack.c: 367 in reftable_stack_reload_maybe_reuse()
________________________________________________________________________________________________________
*** CID 1467030: Security best practices violations (DC.WEAK_CRYPTO)
/reftable/stack.c: 367 in reftable_stack_reload_maybe_reuse()
361 free_names(names_after);
362 return err;
363 }
364 free_names(names);
365 free_names(names_after);
366
>>> CID 1467030: Security best practices violations (DC.WEAK_CRYPTO)
>>> "rand" should not be used for security-related applications, because linear congruential algorithms are too easy to break.
367 delay = delay + (delay * rand()) / RAND_MAX + 1;
368 sleep_millisec(delay);
369 }
370
371 return 0;
372 }
** CID 1467029: Resource leaks (RESOURCE_LEAK)
/reftable/stack.c: 1065 in stack_compact_range()
________________________________________________________________________________________________________
*** CID 1467029: Resource leaks (RESOURCE_LEAK)
/reftable/stack.c: 1065 in stack_compact_range()
1059 }
1060 strbuf_release(&new_table_name);
1061 strbuf_release(&new_table_path);
1062 strbuf_release(&ref_list_contents);
1063 strbuf_release(&temp_tab_file_name);
1064 strbuf_release(&lock_file_name);
>>> CID 1467029: Resource leaks (RESOURCE_LEAK)
>>> Handle variable "lock_file_fd" going out of scope leaks the handle.
1065 return err;
1066 }
1067
1068 int reftable_stack_compact_all(struct reftable_stack *st,
1069 struct reftable_log_expiry_config *config)
1070 {
** CID 1467028: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 675 in reftable_reader_refs_for_indexed()
________________________________________________________________________________________________________
*** CID 1467028: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 675 in reftable_reader_refs_for_indexed()
669 /* read out the reftable_obj_record */
670 reftable_record_from_obj(&got_rec, &got);
671 err = iterator_next(&oit, &got_rec);
672 if (err < 0)
673 goto done;
674
>>> CID 1467028: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "got.hash_prefix" to "memcmp", which dereferences it.
675 if (err > 0 ||
676 memcmp(want.hash_prefix, got.hash_prefix, r->object_id_len)) {
677 /* didn't find it; return empty iterator */
678 iterator_set_empty(it);
679 err = 0;
680 goto done;
** CID 1467027: Security best practices violations (SECURE_TEMP)
/reftable/stack_test.c: 72 in test_read_file()
________________________________________________________________________________________________________
*** CID 1467027: Security best practices violations (SECURE_TEMP)
/reftable/stack_test.c: 72 in test_read_file()
66 return dir;
67 }
68
69 static void test_read_file(void)
70 {
71 char *fn = get_tmp_template(__LINE__);
>>> CID 1467027: Security best practices violations (SECURE_TEMP)
>>> Calling "mkstemp" without securely setting umask first.
72 int fd = mkstemp(fn);
73 char out[1024] = "line1\n\nline2\nline3";
74 int n, err;
75 char **names = NULL;
76 char *want[] = { "line1", "line2", "line3" };
77 int i = 0;
** CID 1467026: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467026: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 683 in reftable_reader_refs_for_indexed()
677 /* didn't find it; return empty iterator */
678 iterator_set_empty(it);
679 err = 0;
680 goto done;
681 }
682
>>> CID 1467026: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "got.offsets" to "new_indexed_table_ref_iter", which dereferences it.
683 err = new_indexed_table_ref_iter(&itr, r, oid, hash_size(r->hash_id),
684 got.offsets, got.offset_len);
685 if (err < 0)
686 goto done;
687 got.offsets = NULL;
688 iterator_from_indexed_table_ref_iter(it, itr);
** CID 1467025: Security best practices violations (SECURE_TEMP)
/reftable/stack.c: 642 in reftable_addition_add()
________________________________________________________________________________________________________
*** CID 1467025: Security best practices violations (SECURE_TEMP)
/reftable/stack.c: 642 in reftable_addition_add()
636 strbuf_reset(&next_name);
637 format_name(&next_name, add->next_update_index, add->next_update_index);
638
639 stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
640 strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
641
>>> CID 1467025: Security best practices violations (SECURE_TEMP)
>>> Calling "mkstemp" without securely setting umask first.
642 tab_fd = mkstemp(temp_tab_file_name.buf);
643 if (tab_fd < 0) {
644 err = REFTABLE_IO_ERROR;
645 goto done;
646 }
647
** CID 1467024: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467024: Null pointer dereferences (FORWARD_NULL)
/reftable/reader.c: 799 in reftable_reader_print_file()
793 if (err < 0)
794 goto done;
795
796 reftable_table_from_reader(&tab, r);
797 err = reftable_table_print(&tab);
798 done:
>>> CID 1467024: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "r" to "reftable_reader_free", which dereferences it.
799 reftable_reader_free(r);
800 return err;
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Fwd: coverity problems in reftable code
[not found] ` <CAFQ2z_OK5949p1WfovJ00Katk5hTv_oeLo-ZRCi1XqrtzQqL2g@mail.gmail.com>
@ 2021-12-07 11:34 ` Han-Wen Nienhuys
2021-12-07 17:46 ` Ævar Arnfjörð Bjarmason
2021-12-08 1:46 ` Jeff King
0 siblings, 2 replies; 7+ messages in thread
From: Han-Wen Nienhuys @ 2021-12-07 11:34 UTC (permalink / raw)
To: git
On Sat, Dec 4, 2021 at 3:13 AM Jeff King <peff@peff.net> wrote:
> We're not doing project-wide analysis with Coverity right now, but I've
> been doing builds of my personal repo, which I usually build off of
> next. And since hn/reftable just hit next, it got included in my latest
> build.
>
> It came up with several complaints. Some of them are dumb and can be
> ignored (e.g., using rand() in a test harness, oh no!) but I poked at a
> few and they look like real issues:
I fixed most of the obvious ones.
> - A lot of your structs have vtables. Initializing them to NULL, as in
> reftable_reader_refs_for_indexed(), leaves the risk that we'll try
> to call a NULL function pointer, even if it's for something simple
I have the impression that coverity doesn't understand enough of the
control flow. Some of the things it complains of are code paths that
only get executed if err==0, in which case, the struct members at hand
should not be null.
> I similarly wondered if these polymorphic types could be using a union
> within reftable_record, rather than pointing to a separate stack
> variable. Then you could initialize the whole thing without worrying
> about intermediate NULLs (and also there's less pointer chasing and it's
> a little bit more type safe than a void pointer). But again, I don't
> know the code well enough to know if that would cover all of your cases.
This is a great idea. I've made a change that does this, which I will
post shortly.
> The summary of issues is below. You can get more details on their site.
> I _think_ I've configured it so that anybody can look at:
>
> https://scan.coverity.com/projects/peff-git/view_defects
Alas, it says I have no access, even after I logged in.
--
Han-Wen Nienhuys - Google Munich
I work 80%. Don't expect answers from me on Fridays.
--
Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Fwd: coverity problems in reftable code
2021-12-07 11:34 ` Fwd: " Han-Wen Nienhuys
@ 2021-12-07 17:46 ` Ævar Arnfjörð Bjarmason
2021-12-08 1:46 ` Jeff King
1 sibling, 0 replies; 7+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2021-12-07 17:46 UTC (permalink / raw)
To: Han-Wen Nienhuys; +Cc: git
On Tue, Dec 07 2021, Han-Wen Nienhuys wrote:
> On Sat, Dec 4, 2021 at 3:13 AM Jeff King <peff@peff.net> wrote:
>> We're not doing project-wide analysis with Coverity right now, but I've
>> been doing builds of my personal repo, which I usually build off of
>> next. And since hn/reftable just hit next, it got included in my latest
>> build.
>>
>> It came up with several complaints. Some of them are dumb and can be
>> ignored (e.g., using rand() in a test harness, oh no!) but I poked at a
>> few and they look like real issues:
>
> I fixed most of the obvious ones.
>
>> - A lot of your structs have vtables. Initializing them to NULL, as in
>> reftable_reader_refs_for_indexed(), leaves the risk that we'll try
>> to call a NULL function pointer, even if it's for something simple
>
> I have the impression that coverity doesn't understand enough of the
> control flow. Some of the things it complains of are code paths that
> only get executed if err==0, in which case, the struct members at hand
> should not be null.
I think coverity is right and the code has a logic error as it
suggests.
In the reftable_reader_refs_for_indexed() example Jeff cites we'll "goto
done" on error, and the reftable_record_release(&got_rec) will proceed
to segfault since the next thing we do is to try to dereference a NULL
pointer in reftable_record_release().
You can reproduce that as a segfault in your tests with e.g. this patch
below, which just emulates what would happen if "err != 0":
diff --git a/reftable/reader.c b/reftable/reader.c
index 006709a645a..4c87b75a982 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -663,6 +663,7 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
/* Look through the reverse index. */
reftable_record_from_obj(&want_rec, &want);
err = reader_seek(r, &oit, &want_rec);
+ goto done;
if (err != 0)
goto done;
In that particular case this appears to be the quick fix that's needed:
diff --git a/reftable/record.c b/reftable/record.c
index 6a5dac32dc6..e6594d555e5 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -1090,6 +1090,8 @@ int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
void reftable_record_release(struct reftable_record *rec)
{
+ if (!rec || !rec->ops)
+ return;
rec->ops->release(rec->data);
}
But in general this looks like an excellent candidate for some test
fuzzing, i.e. to intrstrument the various "err" returning functions to
chaos-monkey return non-zero some of the time and check for segfaults.
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: Fwd: coverity problems in reftable code
2021-12-07 11:34 ` Fwd: " Han-Wen Nienhuys
2021-12-07 17:46 ` Ævar Arnfjörð Bjarmason
@ 2021-12-08 1:46 ` Jeff King
2021-12-08 3:26 ` Jeff King
1 sibling, 1 reply; 7+ messages in thread
From: Jeff King @ 2021-12-08 1:46 UTC (permalink / raw)
To: Han-Wen Nienhuys; +Cc: git
On Tue, Dec 07, 2021 at 12:34:15PM +0100, Han-Wen Nienhuys wrote:
> > - A lot of your structs have vtables. Initializing them to NULL, as in
> > reftable_reader_refs_for_indexed(), leaves the risk that we'll try
> > to call a NULL function pointer, even if it's for something simple
>
> I have the impression that coverity doesn't understand enough of the
> control flow. Some of the things it complains of are code paths that
> only get executed if err==0, in which case, the struct members at hand
> should not be null.
I've definitely run into cases where it doesn't understand some
invariant (e.g., "foo" is only nonzero if "bar" is not NULL). But the
ones I looked at here were triggerable. It's a lot easier to see via
their site which details there view of the control flow...
> > The summary of issues is below. You can get more details on their site.
> > I _think_ I've configured it so that anybody can look at:
> >
> > https://scan.coverity.com/projects/peff-git/view_defects
>
> Alas, it says I have no access, even after I logged in.
...hrmph. I have it "open to all users", but maybe you have to be
associated with the project. I'll send you an "invite" through the
Coverity site and see if that works (of course don't feel obligated if
you don't want to deal further with Coverity; it _does_ produce a ton of
false positives, but it sometimes says useful things, too).
> > I similarly wondered if these polymorphic types could be using a union
> > within reftable_record, rather than pointing to a separate stack
> > variable. Then you could initialize the whole thing without worrying
> > about intermediate NULLs (and also there's less pointer chasing and it's
> > a little bit more type safe than a void pointer). But again, I don't
> > know the code well enough to know if that would cover all of your cases.
>
> This is a great idea. I've made a change that does this, which I will
> post shortly.
Oh good. I was worried I was going off on a tangent. I'll give your
patches a look.
-Peff
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Fwd: coverity problems in reftable code
2021-12-08 1:46 ` Jeff King
@ 2021-12-08 3:26 ` Jeff King
2021-12-08 10:50 ` Han-Wen Nienhuys
0 siblings, 1 reply; 7+ messages in thread
From: Jeff King @ 2021-12-08 3:26 UTC (permalink / raw)
To: Han-Wen Nienhuys; +Cc: git
On Tue, Dec 07, 2021 at 08:46:12PM -0500, Jeff King wrote:
> > > The summary of issues is below. You can get more details on their site.
> > > I _think_ I've configured it so that anybody can look at:
> > >
> > > https://scan.coverity.com/projects/peff-git/view_defects
> >
> > Alas, it says I have no access, even after I logged in.
>
> ...hrmph. I have it "open to all users", but maybe you have to be
> associated with the project. I'll send you an "invite" through the
> Coverity site and see if that works (of course don't feel obligated if
> you don't want to deal further with Coverity; it _does_ produce a ton of
> false positives, but it sometimes says useful things, too).
I also applied your coverity fixups to my tree, and pushed up the
result. As expected, Coverity claims many problems fixed, but also a few
new ones found.
Summary is below, but I'm not sure it's that useful without the whole
code flow. The unreachable-code one seems just wrong. We can get there
via the "goto done" in the BLOCK_TYPE_LOG conditional, it looks like.
The first FORWARD_NULL doesn't look obvious to me from the code. But it
triggers a segfault running "test-tool reftable". (It didn't immediately
for me on Linux, but Windows CI shows it, and compiling with ASan on
Linux does too).
-- >8 --
** CID 1467061: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 356 in test_reftable_obj_record_roundtrip()
________________________________________________________________________________________________________
*** CID 1467061: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 356 in test_reftable_obj_record_roundtrip()
350
351 EXPECT(in.u.obj.hash_prefix_len == out.u.obj.hash_prefix_len);
352 EXPECT(in.u.obj.offset_len == out.u.obj.offset_len);
353
354 EXPECT(!memcmp(in.u.obj.hash_prefix, out.u.obj.hash_prefix,
355 in.u.obj.hash_prefix_len));
>>> CID 1467061: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "out.u.obj.offsets" to "memcmp", which dereferences it.
356 EXPECT(0 == memcmp(in.u.obj.offsets, out.u.obj.offsets,
357 sizeof(uint64_t) * in.u.obj.offset_len));
358 strbuf_release(&key);
359 reftable_record_release(&out);
360 }
361 }
** CID 1467060: Possible Control flow issues (DEADCODE)
/reftable/block.c: 263 in block_reader_init()
________________________________________________________________________________________________________
*** CID 1467060: Possible Control flow issues (DEADCODE)
/reftable/block.c: 263 in block_reader_init()
257 br->header_off = header_off;
258 br->restart_count = restart_count;
259 br->restart_bytes = restart_bytes;
260
261 done:
262 if (uncompressed) {
>>> CID 1467060: Possible Control flow issues (DEADCODE)
>>> Execution cannot reach this statement: "reftable_free(uncompressed);".
263 reftable_free(uncompressed);
264 }
265 return err;
266 }
267
268 static uint32_t block_reader_restart_offset(struct block_reader *br, int i)
** CID 1467059: Null pointer dereferences (FORWARD_NULL)
________________________________________________________________________________________________________
*** CID 1467059: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 155 in test_reftable_ref_record_roundtrip()
149 EXPECT(n > 0);
150
151 /* decode into a non-zero reftable_record to test for leaks. */
152 m = reftable_record_decode(&out, key, i, dest, GIT_SHA1_RAWSZ);
153 EXPECT(n == m);
154
>>> CID 1467059: Null pointer dereferences (FORWARD_NULL)
>>> Passing "&out.u.ref" to "reftable_ref_record_equal", which dereferences null "out.u.ref.refname".
155 EXPECT(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
156 GIT_SHA1_RAWSZ));
157 reftable_record_release(&in);
158
159 strbuf_release(&key);
160 reftable_record_release(&out);
** CID 1467058: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 354 in test_reftable_obj_record_roundtrip()
________________________________________________________________________________________________________
*** CID 1467058: Null pointer dereferences (FORWARD_NULL)
/reftable/record_test.c: 354 in test_reftable_obj_record_roundtrip()
348 GIT_SHA1_RAWSZ);
349 EXPECT(n == m);
350
351 EXPECT(in.u.obj.hash_prefix_len == out.u.obj.hash_prefix_len);
352 EXPECT(in.u.obj.offset_len == out.u.obj.offset_len);
353
>>> CID 1467058: Null pointer dereferences (FORWARD_NULL)
>>> Passing null pointer "out.u.obj.hash_prefix" to "memcmp", which dereferences it.
354 EXPECT(!memcmp(in.u.obj.hash_prefix, out.u.obj.hash_prefix,
355 in.u.obj.hash_prefix_len));
356 EXPECT(0 == memcmp(in.u.obj.offsets, out.u.obj.offsets,
357 sizeof(uint64_t) * in.u.obj.offset_len));
358 strbuf_release(&key);
359 reftable_record_release(&out);
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Fwd: coverity problems in reftable code
2021-12-08 3:26 ` Jeff King
@ 2021-12-08 10:50 ` Han-Wen Nienhuys
2021-12-08 19:12 ` Jeff King
0 siblings, 1 reply; 7+ messages in thread
From: Han-Wen Nienhuys @ 2021-12-08 10:50 UTC (permalink / raw)
To: Jeff King; +Cc: git
On Wed, Dec 8, 2021 at 4:26 AM Jeff King <peff@peff.net> wrote:
> I also applied your coverity fixups to my tree, and pushed up the
> result. As expected, Coverity claims many problems fixed, but also a few
> new ones found.
>
> Summary is below, but I'm not sure it's that useful without the whole
> code flow. The unreachable-code one seems just wrong. We can get there
> via the "goto done" in the BLOCK_TYPE_LOG conditional, it looks like.
>
they're all valid. There is a shadowed variable in the unreachable code one.
> The first FORWARD_NULL doesn't look obvious to me from the code. But it
> triggers a segfault running "test-tool reftable". (It didn't immediately
> for me on Linux, but Windows CI shows it, and compiling with ASan on
> Linux does too).
yeah, that must be the problem on windows too. The 3rd test case
passes NULL into memcmp() with a 0 length.
I've folded in your patch and fixed the issues.
--
Han-Wen Nienhuys - Google Munich
I work 80%. Don't expect answers from me on Fridays.
--
Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Fwd: coverity problems in reftable code
2021-12-08 10:50 ` Han-Wen Nienhuys
@ 2021-12-08 19:12 ` Jeff King
0 siblings, 0 replies; 7+ messages in thread
From: Jeff King @ 2021-12-08 19:12 UTC (permalink / raw)
To: Han-Wen Nienhuys; +Cc: git
On Wed, Dec 08, 2021 at 11:50:16AM +0100, Han-Wen Nienhuys wrote:
> > Summary is below, but I'm not sure it's that useful without the whole
> > code flow. The unreachable-code one seems just wrong. We can get there
> > via the "goto done" in the BLOCK_TYPE_LOG conditional, it looks like.
>
> they're all valid. There is a shadowed variable in the unreachable code one.
Ah, good catch. Yes, that is definitely the problem.
> > The first FORWARD_NULL doesn't look obvious to me from the code. But it
> > triggers a segfault running "test-tool reftable". (It didn't immediately
> > for me on Linux, but Windows CI shows it, and compiling with ASan on
> > Linux does too).
>
> yeah, that must be the problem on windows too. The 3rd test case
> passes NULL into memcmp() with a 0 length.
Ah, makes sense. I didn't look closely, but that would explain perfectly
why it fails on some platforms but not others.
> I've folded in your patch and fixed the issues.
Thanks!
-Peff
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2021-12-08 19:12 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-04 2:13 coverity problems in reftable code Jeff King
[not found] ` <CAFQ2z_OK5949p1WfovJ00Katk5hTv_oeLo-ZRCi1XqrtzQqL2g@mail.gmail.com>
2021-12-07 11:34 ` Fwd: " Han-Wen Nienhuys
2021-12-07 17:46 ` Ævar Arnfjörð Bjarmason
2021-12-08 1:46 ` Jeff King
2021-12-08 3:26 ` Jeff King
2021-12-08 10:50 ` Han-Wen Nienhuys
2021-12-08 19:12 ` Jeff King
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).