From nobody Thu Oct 29 15:56:19 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4jFpkC74qHz6xDx6 for ; Thu, 29 Oct 2026 15:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4jFpkC2lLGz4h3m for ; Thu, 29 Oct 2026 15:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793289379; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4BFNJadZ+94mU3X7bwICG9amGg99jWMMMxbaW3AyQPM=; b=MlgUjenGexmVgAlUtWxl3cdyB8AaNOL0OigUeigRPTOdKNwx8JpBJl8wsWC+mPwxbhwWmx Ip9hbe/5hhrfRPsEsqn4oVzSGvykIH50UHBiaVXMkWmOQX6dY7Ar9uPfEULqmfj4oDHWqW N+3gSwwI96RjJgQ3rSSoeKCK1n05CNsYVBnm7TgnEFesFpXqRgqUD590NysLFFR9cr6gTw 85ETmhhWMZEZs4F0zLUzV6Fdm8BtZt5tjdvuHGVvmVWpvilVQx6OaK0AQ+5zS3/4zcXln7 h18nyGGcSiXQgMWhq1sOEA8b6l++/n+0QSmPGBfyw8Uae9QiDoVh/67Ks9xdmw== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1793289379; b=Ns8PkX2KaEeKA8gF1kxL0Vjod1pJ74qhFZCVbVv7Jb3b0eseJWwpc5+SPhPFZ8Pb/ReYP9 ksBw1EImHJVlhKXA8gHaIlztUDVcK8Ywei93RLWSHB5RRXsq6+09I5pHAKchnfJ6gc1VLl uABmbT2SX9n5c0i9zY3vM5je4iF2Pmvlaxz0scGkguPwukmMNkAZLkTcXc//0OEIJ7ziUR WVGWT376k6QupGOttoPm/ras9LLhJYPCLufuVxFzHgmCkzNbW+Y0BCnYWV1ec64T0Q+eyj ZYxK+p58dlfGEK0szUNjCLKaiW5vcG7rE8k6Gu1J00uBbDrxt/UUL2XY6gbjNQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793289379; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=4BFNJadZ+94mU3X7bwICG9amGg99jWMMMxbaW3AyQPM=; b=T0F8s3Eruy54PlYRS+qiOgxXG3QEWEVRPick8nBcmRdOCugYHq3Vx1O/NA02arHDqo2uzB 6rmkzSnpoLBjveKq6ZTnByIZhwfybjSx3HYlyrYZCoKaE3kpbfnNvhta/nRI7HqBBg+DWs htNB0kOmSHL43VgRUhmL2iE73GOFdVu9ji+OO28hb8qlmTWsZODmR2viVJ1m2992AMYmP8 Fd/5KnawTrErMK/z6UGzc2g5cRcQSXZ7kGXgoQVVCSDCpWi2RCSJh+jV09wYlyAlX1rriu IpufGM1AV6fKwc7SkpXTUtlcgTNzb15z5QRayR/BV9lnHR+y4Bhisu6VGzBfhQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4jFpkC1yC7zmyW for ; Thu, 29 Oct 2026 15:56:19 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 30173 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 29 Oct 2026 15:56:19 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Konstantin Belousov Subject: git: ed059dbfe2ef - main - rtld: free heap donelists abandoned by the lookup restart List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches of the src repository List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kib X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: ed059dbfe2ef85ea79c5bc2834e75b2a33bb8a61 Auto-Submitted: auto-generated Date: Thu, 29 Oct 2026 15:56:19 +0000 Message-Id: <6ae36ca3.30173.d145747@gitrepo.freebsd.org> The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=ed059dbfe2ef85ea79c5bc2834e75b2a33bb8a61 commit ed059dbfe2ef85ea79c5bc2834e75b2a33bb8a61 Author: Konstantin Belousov AuthorDate: 2026-10-25 17:57:40 +0000 Commit: Konstantin Belousov CommitDate: 2026-10-29 15:55:37 +0000 rtld: free heap donelists abandoned by the lookup restart A symbol lookup done under the read-locked rtld_bind_lock is restarted with the write lock when it needs to load filtees. The restart is done by siglongjmp() from lock_restart_for_upgrade(), which skips donelist_free() for the heap donelists of the unwound frames. Only do_dlsym() freed one such donelist, tracked by SymLook.donelist_mem. The donelist of symlook_default() called from _rtld_bind(), the donelist of get_program_var_addr() called for dlopen(RTLD_TRACE), and the donelists of nested symlook_obj_load_filtees() calls were leaked. Link the heap donelists allocated under the read lock into the list owned by RtldLockState, which also owns the sigjmp_buf, instead of the symlook request. Fixes: 1e370f038778 ("rtld: stop using unbound alloca()") Sponsored by: The FreeBSD Foundation MFC after: 1 week --- libexec/rtld-elf/rtld.c | 73 +++++++++++++++++++++++++++++++------------- libexec/rtld-elf/rtld.h | 19 +++++++++--- libexec/rtld-elf/rtld_lock.c | 1 + 3 files changed, 67 insertions(+), 26 deletions(-) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 551e25fb58a5..a98a01b50766 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1039,35 +1039,64 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) * alloca() and we want that to occur within the scope of the caller. */ #define DLP_ALLOCA_LIMIT 100 /* 800 bytes on LP64 */ -#define donelist_init(_DLP, _REQ) do { \ +#define donelist_init(_DLP, _LOCKSTATE) do { \ DoneList *_dlp = _DLP; \ _dlp->num_alloc = obj_count; \ - _dlp->req = NULL; \ if (_dlp->num_alloc > DLP_ALLOCA_LIMIT) { \ - donelist_alloc(_dlp, _REQ); \ + donelist_alloc(_dlp, _LOCKSTATE); \ } else { \ _dlp->objs = alloca(_dlp->num_alloc * sizeof(_dlp->objs[0])); \ + _dlp->mem = NULL; \ } \ _dlp->num_used = 0; \ } while (0) +/* + * A lookup done under the read-locked rtld_bind_lock might be + * restarted by lock_restart_for_upgrade(), which unwinds the stack + * with siglongjmp() and skips donelist_free(). Link the heap + * memory into the list owned by the lockstate, so that + * donelist_free_abandoned() frees it after the restart. + */ static void -donelist_alloc(DoneList *dlp, SymLook *req) +donelist_alloc(DoneList *dlp, RtldLockState *lockstate) { - dlp->objs = xcalloc(dlp->num_alloc, sizeof(dlp->objs[0])); - if (req != NULL && req->donelist_mem == NULL) { - req->donelist_mem = dlp->objs; - dlp->req = req; + struct donelist_mem *dm; + + dm = xmalloc(sizeof(*dm) + dlp->num_alloc * sizeof(dm->objs[0])); + dlp->objs = dm->objs; + dlp->mem = dm; + if (lockstate != NULL && lockstate_rlocked(lockstate)) { + TAILQ_INSERT_HEAD(&lockstate->donelists, dm, next); + dlp->lockstate = lockstate; + } else { + dlp->lockstate = NULL; } } static void donelist_free(DoneList *dlp) { - if (dlp->num_alloc > DLP_ALLOCA_LIMIT) - free(dlp->objs); - if (dlp->req != NULL) - dlp->req->donelist_mem = NULL; + struct donelist_mem *dm; + RtldLockState *lockstate; + + dm = dlp->mem; + if (dm != NULL) { + lockstate = dlp->lockstate; + if (lockstate != NULL) + TAILQ_REMOVE(&lockstate->donelists, dm, next); + } + free(dm); +} + +static void +donelist_free_abandoned(RtldLockState *lockstate) +{ + struct donelist_mem *dm, *dm1; + + TAILQ_FOREACH_SAFE(dm, &lockstate->donelists, next, dm1) + free(dm); + TAILQ_INIT(&lockstate->donelists); } void * @@ -1094,8 +1123,10 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) lockstate_init(&lockstate); relock: rlock_acquire(rtld_bind_lock, &lockstate); - if (sigsetjmp(lockstate.env, 0) != 0) + if (sigsetjmp(lockstate.env, 0) != 0) { lock_upgrade(rtld_bind_lock, &lockstate); + donelist_free_abandoned(&lockstate); + } if (obj->pltrel) rel = (const Elf_Rel *)((const char *)obj->pltrel + reloff); else @@ -3952,8 +3983,10 @@ rtld_dlopen(const char *name, int fd, int mode) if (ld_tracing != NULL) { lockstate_init(&lockstate); rlock_acquire(rtld_bind_lock, &lockstate); - if (sigsetjmp(lockstate.env, 0) != 0) + if (sigsetjmp(lockstate.env, 0) != 0) { lock_upgrade(rtld_bind_lock, &lockstate); + donelist_free_abandoned(&lockstate); + } environ = __DECONST(char **, *get_program_var_addr("environ", &lockstate)); lock_release(rtld_bind_lock, &lockstate); @@ -4152,8 +4185,7 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) { lock_upgrade(rtld_bind_lock, &lockstate); - free(req.donelist_mem); - req.donelist_mem = NULL; + donelist_free_abandoned(&lockstate); } if (handle == NULL || handle == RTLD_NEXT || handle == RTLD_DEFAULT || handle == RTLD_SELF) { @@ -4223,7 +4255,7 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, return (NULL); } - donelist_init(&donelist, &req); + donelist_init(&donelist, &lockstate); if (obj->mainprog) { /* Handle obtained by dlopen(NULL, ...) implies global * scope. */ @@ -4795,7 +4827,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) symlook_init(&req, name); req.lockstate = lockstate; - donelist_init(&donelist, NULL); + donelist_init(&donelist, lockstate); res = NULL; if (symlook_global(&req, &donelist) != 0) goto out; @@ -4885,7 +4917,7 @@ symlook_default(SymLook *req, const Obj_Entry *refobj) SymLook req1; int res; - donelist_init(&donelist, req); + donelist_init(&donelist, req->lockstate); symlook_init_from_req(&req1, req); /* @@ -5024,7 +5056,7 @@ symlook_obj_load_filtees(SymLook *req, SymLook *req1, const Obj_Entry *obj, flags = (req->flags & SYMLOOK_EARLY) != 0 ? RTLD_LO_EARLY : 0; load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate); - donelist_init(&donelist, NULL); + donelist_init(&donelist, req->lockstate); symlook_init_from_req(req1, req); res = symlook_needed(req1, needed, &donelist); donelist_free(&donelist); @@ -6382,7 +6414,6 @@ symlook_init_from_req(SymLook *dst, const SymLook *src) dst->defobj_out = NULL; dst->sym_out = NULL; dst->lockstate = src->lockstate; - dst->donelist_mem = NULL; } static int diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index bdcfc3dc5ded..bd58116cb97a 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -313,20 +313,30 @@ typedef struct Struct_SymCache { const Obj_Entry *obj; /* Shared object which defines it */ } SymCache; +/* + * Heap memory for the DoneList array that is too large for alloca(). + */ +struct donelist_mem { + TAILQ_ENTRY(donelist_mem) next; + const Obj_Entry *objs[]; +}; + /* * This structure provides a reentrant way to keep a list of objects and * check which ones have already been processed in some way. */ typedef struct Struct_DoneList { - const Obj_Entry **objs; /* Array of object pointers */ - struct Struct_SymLook *req; - unsigned int num_alloc; /* Allocated size of the array */ - unsigned int num_used; /* Number of array slots used */ + const Obj_Entry **objs; /* Array of object pointers */ + struct donelist_mem *mem; /* Heap memory for objs */ + struct Struct_RtldLockState *lockstate; /* Lockstate listing mem */ + unsigned int num_alloc; /* Allocated size of the array */ + unsigned int num_used; /* Number of array slots used */ } DoneList; struct Struct_RtldLockState { int lockstate; sigjmp_buf env; + TAILQ_HEAD(, donelist_mem) donelists; }; struct fill_search_info_args { @@ -349,7 +359,6 @@ typedef struct Struct_SymLook { const Obj_Entry *defobj_out; const Elf_Sym *sym_out; struct Struct_RtldLockState *lockstate; - void *donelist_mem; } SymLook; enum { diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index dbb0a9f53287..ae2a15c72652 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -248,6 +248,7 @@ void lockstate_init(RtldLockState *lockstate) { memset(lockstate, 0, sizeof(*lockstate)); + TAILQ_INIT(&lockstate->donelists); } void