Skip to content

Commit f4c4b5d

Browse files
committed
memcollxfrm: Fix a memory leak
This commit creates a new variable, sans_highs, to store newly allocated memory that only happens sometimes. Just before returning, that is freed. This is the final step in solving the leak spotted by Tony Cook in #22811 (comment) The problem was that there were potentially 0 to 3 mallocs in this function, and it was only freeing up to two of them. The solution is to have a separate variable for each malloc, and to free them all before returning. If the corresponding malloc did not happen, the variable will be NULL, and no free will occur. This makes a loop rather more complicated. The next commit will simplify it.
1 parent 3643167 commit f4c4b5d

File tree

1 file changed

+10
-11
lines changed

1 file changed

+10
-11
lines changed

locale.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9642,7 +9642,12 @@ Perl_mem_collxfrm_(pTHX_ const char *input_string,
96429642
/* Must be NUL-terminated */
96439643
assert(*(input_string + len) == '\0');
96449644

9645+
/* We may have to allocate memory to hold modified versions of the input.
9646+
* Initialize to NULL here, and before any return, free them all. Those
9647+
* that do get allocated will be non-NULL then, and get freed */
96459648
char * sans_nuls = NULL; /* NULs changed to lowest collating ctrl */
9649+
char * sans_highs = NULL; /* >0xFF changed to highest collating byte
9650+
for non-UTF8 locales */
96469651
void * free_me = NULL; /* some called functions may allocate memory that
96479652
this function then is required to free */
96489653

@@ -9845,8 +9850,6 @@ Perl_mem_collxfrm_(pTHX_ const char *input_string,
98459850
/* Here, the string is UTF-8, but the locale isn't. strxfrm() is
98469851
* expecting a non-UTF-8 string. Convert the string to bytes. If
98479852
* that succeeds, we are ready to call strxfrm() */
9848-
const char * const t = s; /* Temporary so we can later find where the
9849-
input was */
98509853
utf8 = FALSE;
98519854
if (UNLIKELY(! utf8_to_bytes_new_pv((const U8 **) &s, &len, &free_me)))
98529855
{
@@ -9938,11 +9941,14 @@ Perl_mem_collxfrm_(pTHX_ const char *input_string,
99389941
* that is eaten up by the trailing NUL
99399942
*
99409943
* May shrink; will never grow */
9941-
Newx(s, len, char);
9944+
Newx(sans_highs, len, char);
99429945

99439946
STRLEN i;
99449947
STRLEN d= 0;
9948+
const char * const t = s; /* Temporary so we can later find where
9949+
the input was */
99459950
char * e = (char *) t + len;
9951+
s = sans_highs;
99469952

99479953
for (i = 0; i < len; i+= UTF8SKIP(t + i)) {
99489954
U8 cur_char = t[i];
@@ -9958,13 +9964,6 @@ Perl_mem_collxfrm_(pTHX_ const char *input_string,
99589964
}
99599965
s[d++] = '\0';
99609966
}
9961-
9962-
/* Here, we have constructed a modified version of the input. It could
9963-
* be that we already had a modified copy before we did this version.
9964-
* If so, that copy is no longer needed */
9965-
if (t != input_string) {
9966-
Safefree(t);
9967-
}
99689967
}
99699968
/* else // Here both the locale and string are UTF-8 */
99709969
/* XXX convert above Unicode to 10FFFF? */
@@ -9991,9 +9990,9 @@ Perl_mem_collxfrm_(pTHX_ const char *input_string,
99919990

99929991
# define CLEANUP_STRXFRM_COMMON \
99939992
STMT_START { \
9994-
if (s != input_string && s != sans_nuls && s != free_me) Safefree(s);\
99959993
Safefree(free_me); \
99969994
Safefree(sans_nuls); \
9995+
Safefree(sans_highs); \
99979996
} STMT_END
99989997

99999998
# if defined(USE_POSIX_2008_LOCALE) && defined HAS_STRXFRM_L

0 commit comments

Comments
 (0)