fix(runtime): coverity STRING_NULL #569464 (#34795)

Problem:
Coverity reports string null termination issue:

    *** CID 569464:  Memory - string handling  (STRING_NULL)
    /src/nvim/runtime.c: 1374 in ExpandRTDir_int()
    1372         if (flags & DIP_START) {
    1373           memcpy(tail - 15, "pack/*/start/*/", 15);  // NOLINT
    >>>     CID 569464:  Memory - string handling  (STRING_NULL)
    >>>     Passing unterminated string "tail - 15" to "globpath", which expects a null-terminated string.
    1374           globpath(p_pp, tail - 15, gap, glob_flags, expand_dirs);

    Similar issues occur at lines 1376, 1381, and 1383 where memcpy()
    constructs strings passed to globpath() without null termination.

Solution:
Replace dangerous pointer arithmetic and memcpy() with direct snprintf()
construction of complete search paths. This eliminates the need for
buffer reuse through pointer offsets and ensures all strings passed to
globpath() are properly null-terminated.

vim-patch:9.1.1515: Coverity complains about potential unterminated strings
This commit is contained in:
glepnir
2025-07-06 20:27:20 +08:00
committed by GitHub
parent cf5506f0fd
commit 09f9d72c24

View File

@@ -1351,41 +1351,46 @@ static void ExpandRTDir_int(char *pat, size_t pat_len, int flags, bool keep_ext,
{ {
// TODO(bfredl): this is bullshit, expandpath should not reinvent path logic. // TODO(bfredl): this is bullshit, expandpath should not reinvent path logic.
for (int i = 0; dirnames[i] != NULL; i++) { for (int i = 0; dirnames[i] != NULL; i++) {
const size_t buf_len = strlen(dirnames[i]) + pat_len + 31; const size_t buf_len = strlen(dirnames[i]) + pat_len + 64;
char *const buf = xmalloc(buf_len); char *buf = xmalloc(buf_len);
char *const tail = buf + 15;
const size_t tail_buflen = buf_len - 15;
int glob_flags = 0; int glob_flags = 0;
bool expand_dirs = false; bool expand_dirs = false;
// Build base pattern
if (*dirnames[i] == NUL) { // empty dir used for :runtime snprintf(buf, buf_len, "%s%s%s%s", *dirnames[i] ? dirnames[i] : "", *dirnames[i] ? "/" : "",
snprintf(tail, tail_buflen, "%s*.{vim,lua}", pat); pat, "*.{vim,lua}");
} else {
snprintf(tail, tail_buflen, "%s/%s*.{vim,lua}", dirnames[i], pat);
}
expand: expand:
if ((flags & DIP_NORTP) == 0) { if ((flags & DIP_NORTP) == 0) {
globpath(p_rtp, tail, gap, glob_flags, expand_dirs); globpath(p_rtp, buf, gap, glob_flags, expand_dirs);
} }
if (flags & DIP_START) { if (flags & DIP_START) {
memcpy(tail - 15, "pack/*/start/*/", 15); // NOLINT // pack/*/start/*/ patterns
globpath(p_pp, tail - 15, gap, glob_flags, expand_dirs); snprintf(buf, buf_len, "pack/*/start/*/%s%s%s%s", *dirnames[i] ? dirnames[i] : "", // NOLINT
memcpy(tail - 8, "start/*/", 8); // NOLINT *dirnames[i] ? "/" : "", pat, expand_dirs ? "*" : "*.{vim,lua}");
globpath(p_pp, tail - 8, gap, glob_flags, expand_dirs); globpath(p_pp, buf, gap, glob_flags, expand_dirs);
// start/*/ patterns
snprintf(buf, buf_len, "start/*/%s%s%s%s", *dirnames[i] ? dirnames[i] : "", // NOLINT
*dirnames[i] ? "/" : "", pat, expand_dirs ? "*" : "*.{vim,lua}");
globpath(p_pp, buf, gap, glob_flags, expand_dirs);
} }
if (flags & DIP_OPT) { if (flags & DIP_OPT) {
memcpy(tail - 13, "pack/*/opt/*/", 13); // NOLINT // pack/*/opt/*/ patterns
globpath(p_pp, tail - 13, gap, glob_flags, expand_dirs); snprintf(buf, buf_len, "pack/*/opt/*/%s%s%s%s", *dirnames[i] ? dirnames[i] : "", // NOLINT
memcpy(tail - 6, "opt/*/", 6); // NOLINT *dirnames[i] ? "/" : "", pat, expand_dirs ? "*" : "*.{vim,lua}");
globpath(p_pp, tail - 6, gap, glob_flags, expand_dirs); globpath(p_pp, buf, gap, glob_flags, expand_dirs);
// opt/*/ patterns
snprintf(buf, buf_len, "opt/*/%s%s%s%s", *dirnames[i] ? dirnames[i] : "", // NOLINT
*dirnames[i] ? "/" : "", pat, expand_dirs ? "*" : "*.{vim,lua}");
globpath(p_pp, buf, gap, glob_flags, expand_dirs);
} }
// Second round for directories
if (*dirnames[i] == NUL && !expand_dirs) { if (*dirnames[i] == NUL && !expand_dirs) {
// expand dir names in another round snprintf(buf, buf_len, "%s*", pat);
snprintf(tail, tail_buflen, "%s*", pat);
glob_flags = WILD_ADD_SLASH; glob_flags = WILD_ADD_SLASH;
expand_dirs = true; expand_dirs = true;
goto expand; goto expand;