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.
for (int i = 0; dirnames[i] != NULL; i++) {
const size_t buf_len = strlen(dirnames[i]) + pat_len + 31;
char *const buf = xmalloc(buf_len);
char *const tail = buf + 15;
const size_t tail_buflen = buf_len - 15;
const size_t buf_len = strlen(dirnames[i]) + pat_len + 64;
char *buf = xmalloc(buf_len);
int glob_flags = 0;
bool expand_dirs = false;
if (*dirnames[i] == NUL) { // empty dir used for :runtime
snprintf(tail, tail_buflen, "%s*.{vim,lua}", pat);
} else {
snprintf(tail, tail_buflen, "%s/%s*.{vim,lua}", dirnames[i], pat);
}
// Build base pattern
snprintf(buf, buf_len, "%s%s%s%s", *dirnames[i] ? dirnames[i] : "", *dirnames[i] ? "/" : "",
pat, "*.{vim,lua}");
expand:
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) {
memcpy(tail - 15, "pack/*/start/*/", 15); // NOLINT
globpath(p_pp, tail - 15, gap, glob_flags, expand_dirs);
memcpy(tail - 8, "start/*/", 8); // NOLINT
globpath(p_pp, tail - 8, gap, glob_flags, expand_dirs);
// pack/*/start/*/ patterns
snprintf(buf, buf_len, "pack/*/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);
// 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) {
memcpy(tail - 13, "pack/*/opt/*/", 13); // NOLINT
globpath(p_pp, tail - 13, gap, glob_flags, expand_dirs);
memcpy(tail - 6, "opt/*/", 6); // NOLINT
globpath(p_pp, tail - 6, gap, glob_flags, expand_dirs);
// pack/*/opt/*/ patterns
snprintf(buf, buf_len, "pack/*/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);
// 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) {
// expand dir names in another round
snprintf(tail, tail_buflen, "%s*", pat);
snprintf(buf, buf_len, "%s*", pat);
glob_flags = WILD_ADD_SLASH;
expand_dirs = true;
goto expand;