From 5a7ce2733a2d9d9fccad4dd48047546000d9994d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 24 Jul 2026 22:51:53 +0000 Subject: [PATCH] patch 9.2.0854: memory leak when reading a spell file with SN_SAL and SN_SOFO Problem: Memory leak when a spell file has an SN_SAL section before an SN_SOFO section: set_sofo() reuses sl_sal without freeing the salitem_T entries left by read_sal_section() (after v9.2.0846). Solution: Factor the SAL free loop into free_sal_items() and call it before set_sofo() reuses sl_sal. closes: #20836 Supported by AI. Signed-off-by: Christian Brabandt --- src/proto/spell.pro | 1 + src/spell.c | 39 ++++++++++++++++++++++++++------------- src/spellfile.c | 2 ++ src/version.c | 2 ++ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/proto/spell.pro b/src/proto/spell.pro index a68de8f1d8..a7e547bf40 100644 --- a/src/proto/spell.pro +++ b/src/proto/spell.pro @@ -11,6 +11,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen); char_u *spell_enc(void); slang_T *slang_alloc(char_u *lang); void slang_free(slang_T *lp); +void free_sal_items(garray_T *gap); void slang_clear(slang_T *lp); void slang_clear_sug(slang_T *lp); void count_common_word(slang_T *lp, char_u *word, int len, int count); diff --git a/src/spell.c b/src/spell.c index 1276604251..de807d9053 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1752,6 +1752,30 @@ slang_free(slang_T *lp) vim_free(lp); } + +/* + * Free the salitem_T entries in a "sl_sal" garray (the SN_SAL form) and + * clear the garray. Used by slang_clear() and when set_sofo() reuses + * sl_sal for the SN_SOFO form. + */ + void +free_sal_items(garray_T *gap) +{ + salitem_T *smp; + + while (gap->ga_len > 0) + { + smp = &((salitem_T *)gap->ga_data)[--gap->ga_len]; + vim_free(smp->sm_lead); + // Don't free sm_oneof and sm_rules, they point into sm_lead. + vim_free(smp->sm_to); + vim_free(smp->sm_lead_w); + vim_free(smp->sm_oneof_w); + vim_free(smp->sm_to_w); + } + ga_clear(gap); +} + /* * Clear an slang_T so that the file can be reloaded. */ @@ -1760,7 +1784,6 @@ slang_clear(slang_T *lp) { garray_T *gap; fromto_T *ftp; - salitem_T *smp; int i; int round; @@ -1792,20 +1815,10 @@ slang_clear(slang_T *lp) // SOFOFROM and SOFOTO items: free lists of wide characters. for (i = 0; i < gap->ga_len; ++i) vim_free(((int **)gap->ga_data)[i]); + ga_clear(gap); } else - // SAL items: free salitem_T items - while (gap->ga_len > 0) - { - smp = &((salitem_T *)gap->ga_data)[--gap->ga_len]; - vim_free(smp->sm_lead); - // Don't free sm_oneof and sm_rules, they point into sm_lead. - vim_free(smp->sm_to); - vim_free(smp->sm_lead_w); - vim_free(smp->sm_oneof_w); - vim_free(smp->sm_to_w); - } - ga_clear(gap); + free_sal_items(gap); for (i = 0; i < lp->sl_prefixcnt; ++i) vim_regfree(lp->sl_prefprog[i]); diff --git a/src/spellfile.c b/src/spellfile.c index e90c89b73d..67fe17502e 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -1421,6 +1421,8 @@ set_sofo(slang_T *lp, char_u *from, char_u *to) if (has_mbyte) { + free_sal_items(&lp->sl_sal); + // Use "sl_sal" as an array with 256 pointers to a list of wide // characters. The index is the low byte of the character. // The list contains from-to pairs with a terminating NUL. diff --git a/src/version.c b/src/version.c index 7601e516d0..4808dfa6d0 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 854, /**/ 853, /**/ -- 2.47.3