Bug 6660 - Small memory leak after calling several times lp_update
Summary: Small memory leak after calling several times lp_update
Status: RESOLVED FIXED
Alias: None
Product: Samba 4.0
Classification: Unclassified
Component: Other (show other bugs)
Version: unspecified
Hardware: Other Linux
: P3 minor (vote)
Target Milestone: ---
Assignee: Andrew Bartlett
QA Contact: Matthias Dieter Wallnöfer
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-08-25 15:13 UTC by inra
Modified: 2009-09-07 02:44 UTC (History)
0 users

See Also:


Attachments
samba4alpha7 (modified dprintf.c) (2.55 KB, text/plain)
2009-08-28 11:48 UTC, inra
no flags Details
samba4alpha7 (modified iconv.c) (18.17 KB, text/plain)
2009-08-28 11:49 UTC, inra
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description inra 2009-08-25 15:13:13 UTC
Calling lp_update (loadparm.c) several times causes a small memory leak.

	if (strcmp(lp_display_charset(lp_ctx), lp_unix_charset(lp_ctx)) != 0)
		d_set_iconv(smb_iconv_open(lp_display_charset(lp_ctx), lp_unix_charset(lp_ctx)));
	else
		d_set_iconv((smb_iconv_t)-1); 

smb_iconv_open (iconv.c) tallocates a memory chunk in NULL (global) context (maybe it would be better to use talloc_autofree_context()) . This pointer is passed to d_set_iconv (dprintf.c) which looks like this:

void d_set_iconv(smb_iconv_t cd)
{
	display_cd = cd;
} 

I think that it would be better for this function to looks like this:

void d_set_iconv(smb_iconv_t cd)
{
	if (display_cd != (smb_iconv_t) -1)
		talloc_free(display_cd); // no memory leaks here in case several calls
	display_cd = cd;
}
Comment 1 inra 2009-08-26 09:54:35 UTC
I did some more test with valgrind and above/below fix is not enough. Additional changes must be done in iconv.c - smb_iconv_open_ex in native mode can call system function iconv_open which allocates some memory. So talloc_free(display_cd) in function d_set_iconv would free talloced memory but won't free iconv_open resources. To do this i've added:

int smb_iconv_t_destructor(smb_iconv_t hwd)
{ 
#ifdef HAVE_NATIVE_ICONV
    if (hwd->cd_pull != NULL && hwd->cd_pull != (iconv_t)-1)
        iconv_close(hwd->cd_pull);
    if (hwd->cd_push != NULL && hwd->cd_push != (iconv_t)-1)
        iconv_close(hwd->cd_push);
    if (hwd->cd_direct != NULL && hwd->cd_direct != (iconv_t)-1)
        iconv_close(hwd->cd_direct);
#endif
    
	return 0;     
} 

and then i've modified smb_iconv_open_ex, smb_iconv_open and smb_iconv_close like this:

_PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, 
			      const char *fromcode, bool native_iconv)
{
	smb_iconv_t ret;
	const struct charset_functions *from=NULL, *to=NULL;
	int i;

	ret = (smb_iconv_t)talloc_named(mem_ctx,
					sizeof(*ret), 
					"iconv(%s,%s)", tocode, fromcode);
	if (!ret) {
		errno = ENOMEM;
		return (smb_iconv_t)-1;
	}
	memset(ret, 0, sizeof(*ret));
    talloc_set_destructor(ret, smb_iconv_t_destructor); 

	/* check for the simplest null conversion */
	if (strcmp(fromcode, tocode) == 0) {
		ret->direct = iconv_copy;
		return ret;
	}

	for (i=0;i<ARRAY_SIZE(builtin_functions);i++) {
		if (strcasecmp(fromcode, builtin_functions[i].name) == 0) {
			from = &builtin_functions[i];
		}
		if (strcasecmp(tocode, builtin_functions[i].name) == 0) {
			to = &builtin_functions[i];
		}
	}

	if (from == NULL) {
		for (from=charsets; from; from=from->next) {
			if (strcasecmp(from->name, fromcode) == 0) break;
		}
	}

	if (to == NULL) {
		for (to=charsets; to; to=to->next) {
			if (strcasecmp(to->name, tocode) == 0) break;
		}
	}

#ifdef HAVE_NATIVE_ICONV
	if ((!from || !to) && !native_iconv) {
		goto failed;
	}
	if (!from) {
		ret->pull = sys_iconv;
		ret->cd_pull = iconv_open("UTF-16LE", fromcode);
		if (ret->cd_pull == (iconv_t)-1)
			ret->cd_pull = iconv_open("UCS-2LE", fromcode);
		if (ret->cd_pull == (iconv_t)-1) goto failed;
	}

	if (!to) {
		ret->push = sys_iconv;
		ret->cd_push = iconv_open(tocode, "UTF-16LE");
		if (ret->cd_push == (iconv_t)-1)
			ret->cd_push = iconv_open(tocode, "UCS-2LE");
		if (ret->cd_push == (iconv_t)-1) goto failed;
	}
#else
	if (!from || !to) {
		goto failed;
	}
#endif

	/* check for conversion to/from ucs2 */
	if (is_utf16(fromcode) && to) {
		ret->direct = to->push;
		return ret;
	}
	if (is_utf16(tocode) && from) {
		ret->direct = from->pull;
		return ret;
	}

#ifdef HAVE_NATIVE_ICONV
	if (is_utf16(fromcode)) {
		ret->direct = sys_iconv;
		ret->cd_direct = ret->cd_push;
		ret->cd_push = NULL;
		return ret;
	}
	if (is_utf16(tocode)) {
		ret->direct = sys_iconv;
        /* in my opinion this could be set just above (UTF-16LE to UCS-2LE) - so we need to close iconv */
        if (ret->cd_direct != NULL && ret->cd_direct != (iconv_t)-1)
            iconv_close(ret->cd_direct);
		ret->cd_direct = ret->cd_pull;
		ret->cd_pull = NULL;
		return ret;
	}
#endif

	/* the general case has to go via a buffer */
	if (!ret->pull) ret->pull = from->pull;
	if (!ret->push) ret->push = to->push;
	return ret;

failed:
	talloc_free(ret);
	errno = EINVAL;
	return (smb_iconv_t)-1;
}

/*
  simple iconv_open() wrapper
 */
_PUBLIC_ smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode)
{
	return smb_iconv_open_ex(talloc_autofree_context(), tocode, fromcode, true);
}

/*
  simple iconv_close() wrapper
*/
_PUBLIC_ int smb_iconv_close(smb_iconv_t cd)
{
	talloc_free(cd);
	return 0;
} 

Comment 2 Matthias Dieter Wallnöfer 2009-08-28 04:33:14 UTC
Very interesting issue. It is possible for you to give us a patch in the diff format (you could upload it here as plaintext)?
Comment 3 inra 2009-08-28 11:48:23 UTC
Created attachment 4604 [details]
samba4alpha7 (modified dprintf.c)

note that mine work coding standard for tab is 4 spaces - not tabs
Comment 4 inra 2009-08-28 11:49:40 UTC
Created attachment 4605 [details]
samba4alpha7 (modified iconv.c)

note that mine work coding standard for tab is 4 spaces - not tabs
Comment 5 inra 2009-08-28 11:55:52 UTC
I've attached changed files. I'm sorry for not including patch files but i don't know a solid program for W2K to create one. I also apologize but i still use alpha7 (not alpha8) - so both files are alpha7 native (but there there are minor changes between both versions in those files).
Comment 6 Matthias Dieter Wallnöfer 2009-09-07 02:44:40 UTC
Applied, thanks!