From patchwork Thu Apr 5 05:32:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 26610 Received: (qmail 108136 invoked by alias); 5 Apr 2018 05:32:41 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 108124 invoked by uid 89); 5 Apr 2018 05:32:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=dlvsym X-HELO: mx1.redhat.com Date: Thu, 05 Apr 2018 01:32:37 -0400 Message-Id: From: DJ Delorie To: libc-alpha@sourceware.org Subject: [patch] yet another memory leak, in dlfcn this time While doing the work on extracting the malloc hooks, I discovered another place where memory needs to be free'd for leak checkers. Unable to reproduce with valgrind but that might just mean valgrind uses a different technique to interpose the calls. This one triggers if you call dlfcn() during LD_PRELOAD. The hook might be a bit hacky, but then again, all the freeres hook stuff is a bit hacky. * dlfcn/Makefile: Add dlfreeres and libc_dlfreeres. * dlfcn/Versions: Add __libdl_freeres as GLIBC_PRIVATE. * dlfcn/dlerror.c (dlerror_main_freeres): New. * dlfcn/dlfreeres.c: New. * dlfcn/sdlfreeres.c: New. * malloc/set-freeres.c: Add hook for libdl.so diff --git a/dlfcn/Makefile b/dlfcn/Makefile index 56dcae0604..34f9923334 100644 --- a/dlfcn/Makefile +++ b/dlfcn/Makefile @@ -22,7 +22,7 @@ include ../Makeconfig headers := bits/dlfcn.h dlfcn.h extra-libs := libdl libdl-routines := dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \ - dlmopen dlfcn + dlmopen dlfcn dlfreeres routines := $(patsubst %,s%,$(filter-out dlfcn,$(libdl-routines))) elide-routines.os := $(routines) diff --git a/dlfcn/Versions b/dlfcn/Versions index 97902f0dfd..1df6925a92 100644 --- a/dlfcn/Versions +++ b/dlfcn/Versions @@ -13,5 +13,6 @@ libdl { } GLIBC_PRIVATE { _dlfcn_hook; + __libdl_freeres; } } diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c index 04dce9ddc6..8111c69dcb 100644 --- a/dlfcn/dlerror.c +++ b/dlfcn/dlerror.c @@ -215,13 +215,27 @@ static void free_key_mem (void *mem) { check_free ((struct dl_action_result *) mem); - free (mem); __libc_setspecific (key, NULL); } # ifdef SHARED +void +dlerror_main_freeres (void) +{ + void *mem; + + mem = __libc_getspecific (key); + + if (mem) + { + free (mem); + __libc_setspecific (key, NULL); + } +} +text_set_element (libdl_freeres_hooks, dlerror_main_freeres); + struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon)); libdl_hidden_data_def (_dlfcn_hook) diff --git a/dlfcn/dlfreeres.c b/dlfcn/dlfreeres.c new file mode 100644 index 0000000000..567630a2e6 --- /dev/null +++ b/dlfcn/dlfreeres.c @@ -0,0 +1,23 @@ +/* Clean up allocated libdl memory on demand. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +DEFINE_HOOK (libdl_freeres_hooks, (void)) + +DEFINE_HOOK_RUNNER (libdl_freeres_hooks, __libdl_freeres, (void), ()) diff --git a/dlfcn/sdlfreeres.c b/dlfcn/sdlfreeres.c new file mode 100644 index 0000000000..7347672990 --- /dev/null +++ b/dlfcn/sdlfreeres.c @@ -0,0 +1 @@ +#include "dlfreeres.c" diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c index f4a0e7bda4..a74f6bd54b 100644 --- a/malloc/set-freeres.c +++ b/malloc/set-freeres.c @@ -26,6 +26,8 @@ DEFINE_HOOK (__libc_subfreeres, (void)); symbol_set_define (__libc_freeres_ptrs); +extern __attribute__((weak)) void __libdl_freeres (void); + void __libc_freeres_fn_section __libc_freeres (void) { @@ -41,6 +43,10 @@ __libc_freeres (void) RUN_HOOK (__libc_subfreeres, ()); + /* This hooks into libdl.so's table of things-to-be-freed. */ + if (__libdl_freeres) + __libdl_freeres (); + for (p = symbol_set_first_element (__libc_freeres_ptrs); !symbol_set_end_p (__libc_freeres_ptrs, p); ++p) free (*p);