From aaf57a053d3d623515a409ee3a5cc3539c94a936 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 16 Aug 2026 11:37:51 -0400 Subject: [PATCH] fix(coverity): false positives in kvec usages #41341 Coverity can't follow kv_ensure_space()'s `kv_roundup32()` bit math, so every kv_concat_len() looks like an overrun; and it doesn't know `kv_push()` allocates when `size == capacity`. _____________________________________________________________________________________________ CID 653191: Memory - illegal accesses (OVERRUN) /src/nvim/input.c: 3536 in paste_store() 3530 3531 if (s > start) { 3532 if (need_redo) { 3533 kv_concat_len(redobuff.cur.keys, start, (size_t)(s - start)); 3534 } 3535 if (need_record) { >>> CID 653191: Memory - illegal accesses (OVERRUN) >>> Overrunning dynamic array "recordbuff.items" at offset corresponding to index variable "recordbuff.size" through dereference in call to "memcpy". 3536 kv_concat_len(recordbuff, start, (size_t)(s - start)); 3537 } 3538 } 3539 3540 if (s < str_end) { 3541 int c = (uint8_t)(*s++); _____________________________________________________________________________________________ CID 653190: Memory - illegal accesses (OVERRUN) /src/nvim/input.c: 730 in redo_append_spec() 724 return; 725 } 726 727 while (*s != NUL) { 728 if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { 729 // Insert special key literally. >>> CID 653190: Memory - illegal accesses (OVERRUN) >>> Overrunning dynamic array "redobuff.cur.keys.items" at offset corresponding to index variable "redobuff.cur.keys.size" through dereference in call to "memcpy". 730 kv_concat_len(redobuff.cur.keys, s, 3); 731 s += 3; 732 } else { 733 sb_add_char(&redobuff.cur.keys, mb_cptr2char_adv(&s)); 734 } 735 } CID 653189: (OVERRUN) /src/nvim/input.c: 3533 in paste_store() /src/nvim/input.c: 3536 in paste_store() _____________________________________________________________________________________________ CID 653189: (OVERRUN) /src/nvim/input.c: 3533 in paste_store() 3527 && *s != NL && !(crlf && *s == CAR)) { 3528 s++; 3529 } 3530 3531 if (s > start) { 3532 if (need_redo) { >>> CID 653189: (OVERRUN) >>> Overrunning dynamic array "redobuff.cur.keys.items" at offset corresponding to index variable "redobuff.cur.keys.size" through dereference in call to "memcpy". 3533 kv_concat_len(redobuff.cur.keys, start, (size_t)(s - start)); 3534 } 3535 if (need_record) { 3536 kv_concat_len(recordbuff, start, (size_t)(s - start)); 3537 } 3538 } /src/nvim/input.c: 3536 in paste_store() 3530 3531 if (s > start) { 3532 if (need_redo) { 3533 kv_concat_len(redobuff.cur.keys, start, (size_t)(s - start)); 3534 } 3535 if (need_record) { >>> CID 653189: (OVERRUN) >>> Overrunning dynamic array "recordbuff.items" at offset corresponding to index variable "recordbuff.size" through dereference in call to "memcpy". 3536 kv_concat_len(recordbuff, start, (size_t)(s - start)); 3537 } 3538 } 3539 3540 if (s < str_end) { 3541 int c = (uint8_t)(*s++); _____________________________________________________________________________________________ CID 653188: Memory - illegal accesses (OVERRUN) /src/nvim/input_cmdatom.c: 216 in atoms_concat_keys() 210 211 /// Concatenates the keys of multiple atoms into one (allocated) string. 212 static String atoms_concat_keys(CmdAtomVec atoms) 213 { 214 StringBuilder keys = KV_INITIAL_VALUE; 215 for (size_t i = 0; i < kv_size(atoms); i++) { >>> CID 653188: Memory - illegal accesses (OVERRUN) >>> Overrunning dynamic array "keys.items" at offset corresponding to index variable "keys.size" through dereference in call to "memcpy". 216 kv_concat(keys, kv_A(atoms, i).keys); 217 } 218 size_t len = kv_size(keys); 219 kv_push(keys, NUL); 220 return (String){ .data = keys.items, .size = len }; 221 } CID 653187: Null pointer dereferences (FORWARD_NULL) _____________________________________________________________________________________________ CID 653186: Null pointer dereferences (FORWARD_NULL) /src/nvim/input_cmdatom.c: 173 in atom_compose_keys() 167 StringBuilder sb = KV_INITIAL_VALUE; 168 redo_prefix(&spec, &sb, false); 169 redo_chars(&spec, &sb, false); 170 if (sb.size == 0) { 171 return NULL; 172 } >>> CID 653186: Null pointer dereferences (FORWARD_NULL) >>> Dereferencing null pointer "((sb.size == sb.capacity) ? (sb.capacity = (sb.capacity ? sb.capacity << 1 : 8UL)) , (sb.items = xrealloc(sb.items, 1UL * sb.capacity)) , 0 : 0) , (sb.items + sb.size++)". 173 kv_push(sb, NUL); 174 return sb.items; 175 } 176 177 /// The pending change as a CmdAtom: the composed keysequence plus the structured fields. 178 /// Caller owns `keys`. _____________________________________________________________________________________________ CID 653185: Null pointer dereferences (FORWARD_NULL) /src/nvim/input.c: 296 in redo_compose() 290 StringBuilder buf = KV_INITIAL_VALUE; 291 redo_prefix(&r->spec, &buf, false); 292 kv_splice(buf, r->keys); 293 if (buf.size == 0) { 294 return (String)STRING_INIT; 295 } >>> CID 653185: Null pointer dereferences (FORWARD_NULL) >>> Dereferencing null pointer "((buf.size == buf.capacity) ? (buf.capacity = (buf.capacity ? buf.capacity << 1 : 8UL)) , (buf.items = xrealloc(buf.items, 1UL * buf.capacity)) , 0 : 0) , (buf.items + buf.size++)". 296 kv_push(buf, NUL); 297 return cbuf_as_string(buf.items, buf.size - 1); 298 } 299 301 String redo_keys(void) _____________________________________________________________________________________________ CID 653184: Memory - illegal accesses (OVERRUN) /src/nvim/input.c: 3533 in paste_store() 3527 && *s != NL && !(crlf && *s == CAR)) { 3528 s++; 3529 } 3530 3531 if (s > start) { 3532 if (need_redo) { >>> CID 653184: Memory - illegal accesses (OVERRUN) >>> Overrunning dynamic array "redobuff.cur.keys.items" at offset corresponding to index variable "redobuff.cur.keys.size" through dereference in call to "memcpy". 3533 kv_concat_len(redobuff.cur.keys, start, (size_t)(s - start)); 3534 } 3535 if (need_record) { 3536 kv_concat_len(recordbuff, start, (size_t)(s - start)); 3537 } 3538 } _____________________________________________________________________________________________ CID 653183: Memory - illegal accesses (OVERRUN) /src/nvim/input.c: 730 in redo_append_spec() 724 return; 725 } 726 727 while (*s != NUL) { 728 if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { 729 // Insert special key literally. >>> CID 653183: Memory - illegal accesses (OVERRUN) >>> Overrunning dynamic array "redobuff.cur.keys.items" at offset corresponding to index variable "redobuff.cur.keys.size" through dereference in call to "memcpy". 730 kv_concat_len(redobuff.cur.keys, s, 3); 731 s += 3; 732 } else { 733 sb_add_char(&redobuff.cur.keys, mb_cptr2char_adv(&s)); 734 } --- src/klib/kvec.h | 2 ++ src/nvim/input.c | 2 ++ src/nvim/input_cmdatom.c | 1 + 3 files changed, 5 insertions(+) diff --git a/src/klib/kvec.h b/src/klib/kvec.h index fe1ae6a04d..dc8b08c6ef 100644 --- a/src/klib/kvec.h +++ b/src/klib/kvec.h @@ -108,6 +108,8 @@ if (len > 0) { \ kv_ensure_space(v, len); \ assert((v).items); \ + /* kv_roundup32() only ORs bits in, so kv_ensure_space() leaves capacity >= size+len. */ \ + /* coverity[overrun-buffer-arg] */ \ memcpy((v).items + (v).size, data, sizeof((v).items[0]) * len); \ (v).size = (v).size + len; \ } diff --git a/src/nvim/input.c b/src/nvim/input.c index b6d53499bf..9db26f6d97 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -293,6 +293,7 @@ static String redo_compose(RedoBuf *r) if (buf.size == 0) { return (String)STRING_INIT; } + assert(buf.items != NULL); // Coverity false-positive (already checked `size` above). kv_push(buf, NUL); return cbuf_as_string(buf.items, buf.size - 1); } @@ -865,6 +866,7 @@ int start_redo(int count, bool old_redo) } StringBuilder prefix = KV_INITIAL_VALUE; redo_prefix(&spec, &prefix, true); + // coverity[var_deref_model] `items` is NULL only when size=0, which add_buff() ignores. add_buff(&readbuf2, prefix.items, (ptrdiff_t)prefix.size); kv_destroy(prefix); if (spec.regname == '=') { diff --git a/src/nvim/input_cmdatom.c b/src/nvim/input_cmdatom.c index 42e0817339..f0974403cb 100644 --- a/src/nvim/input_cmdatom.c +++ b/src/nvim/input_cmdatom.c @@ -170,6 +170,7 @@ static char *atom_compose_keys(CmdSpec spec) if (sb.size == 0) { return NULL; } + assert(sb.items != NULL); // Coverity false-positive (already checked `size` above). kv_push(sb, NUL); return sb.items; }