From 35cbf4fbddc5b095bc21fe23fd62f091355e40b0 Mon Sep 17 00:00:00 2001 From: Muhammad Arrafif Prikusuma Date: Sun, 28 Jun 2026 07:08:59 +0800 Subject: [PATCH] fix(autocmd): safely parse buflocal buffer numbers (#40436) * refactor(autocmd): safely parse buflocal buffer numbers Replace unsafe atoi with a robust integer conversion when extracting the buffer number out of a "" pattern in aupat\_get\_buflocal\_nr. This prevents potential silent integer overflows and ensures variables fit safely within INT\_MAX boundaries before truncation. * fix(autocmd): use getdigits_int to safely parse buffer numbers * fix(autocmd): raise error for invalid buffer-local pattern values When defining an autocommand, an invalid buffer-local pattern format (such as `` or ``) would previously fall through silently and default to a generic `` tag behavior. Fix this regression by trapping invalid buffer numbers early from `aupat_get_buflocal_nr()` and throwing a proper E680 error to match Vim's runtime validation behavior. --- src/nvim/autocmd.c | 17 ++++++++++------- src/nvim/errors.h | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7a13399c6b..e154095001 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -931,6 +931,10 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, cons if (is_buflocal) { const int buflocal_nr = aupat_get_buflocal_nr(pat, patlen); + if (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL) { + semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr); + return FAIL; + } // normalize pat into standard "#N" form aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr); @@ -1000,6 +1004,10 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int if (is_buflocal) { buflocal_nr = aupat_get_buflocal_nr(pat, patlen); + if (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL) { + semsg(_(e_buffer_nr_invalid_buffer_number), buflocal_nr); + return FAIL; + } // normalize pat into standard "#N" form aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr); @@ -1025,12 +1033,6 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int // No matching pattern found, allocate a new one. if (ap == NULL) { - // refuse to add buffer-local ap if buffer number is invalid - if (is_buflocal && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) { - semsg(_("E680: : invalid buffer number "), buflocal_nr); - return FAIL; - } - ap = xmalloc(sizeof(AutoPat)); if (is_buflocal) { @@ -2592,7 +2594,8 @@ int aupat_get_buflocal_nr(const char *pat, int patlen) // "" if (skipdigits(pat + 8) == pat + patlen - 1) { - return atoi(pat + 8); + char *p = (char *)pat + 8; + return getdigits_int(&p, false, 0); } } diff --git a/src/nvim/errors.h b/src/nvim/errors.h index 1ad98f2aeb..9fdb6a6895 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -18,6 +18,7 @@ EXTERN const char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; : invalid buffer number")); EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded")); EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif")); EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry"));