newlocale(3): Fix a memory leak.

newlocale() optionally takes a "base" locale, from which components not
specified in the mask are inherited.  POSIX says that newlocale() may
modify "base" and return it, or free "base" and return a newly allocated
locale.  We were not doing either, so applications which use newlocale()
to modify an existing base locale end up leaking memory on FreeBSD.

This diff fixes the leak by releasing a reference to the base locale
before returning.  This is less efficient than modifying "base"
directly, but is simpler for an initial bug fix.  Also, update the man
page to clarify behaviour with respect to "base".

PR:		249416
MFC after:	3 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D26522
This commit is contained in:
Mark Johnston 2020-10-02 18:35:55 +00:00
parent 1e145e73b8
commit afd95785c0
2 changed files with 19 additions and 2 deletions

View File

@ -26,7 +26,7 @@
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.Dd September 17, 2011
.Dd October 2, 2020
.Dt NEWLOCALE 3
.Os
.Sh NAME
@ -46,7 +46,20 @@ defines the components that the new locale will have set to the locale with the
name specified in the
.Fa locale
parameter.
Any other components will be inherited from
Any components not specified in
.Fa mask
will be inherited from the locale referenced by
.Fa base ,
if
.Fa base
is not
.Dv NULL .
If the call is successful, the state of the locale referenced by
.Fa base
is unspecified, and it must not be accessed.
The special locale
.Dv LC_GLOBAL_LOCALE
may not be specified for
.Fa base .
The
.Fa mask

View File

@ -251,6 +251,7 @@ static int dupcomponent(int type, locale_t base, locale_t new)
locale_t newlocale(int mask, const char *locale, locale_t base)
{
locale_t orig_base;
int type;
const char *realLocale = locale;
int useenv = 0;
@ -263,6 +264,7 @@ locale_t newlocale(int mask, const char *locale, locale_t base)
return (NULL);
}
orig_base = base;
FIX_LOCALE(base);
copyflags(new, base);
@ -297,6 +299,8 @@ locale_t newlocale(int mask, const char *locale, locale_t base)
if (0 == success) {
xlocale_release(new);
new = NULL;
} else if (base == orig_base) {
xlocale_release(base);
}
return (new);