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; }
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; }
Very interesting issue. It is possible for you to give us a patch in the diff format (you could upload it here as plaintext)?
Created attachment 4604 [details] samba4alpha7 (modified dprintf.c) note that mine work coding standard for tab is 4 spaces - not tabs
Created attachment 4605 [details] samba4alpha7 (modified iconv.c) note that mine work coding standard for tab is 4 spaces - not tabs
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).
Applied, thanks!