git: 682ebe10032d - stable/15 - rtld: stop using unbound alloca()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 06 Jul 2026 01:06:17 UTC
The branch stable/15 has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=682ebe10032d4e682381eb5f108a8297be7b682c
commit 682ebe10032d4e682381eb5f108a8297be7b682c
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-06-27 15:11:37 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-06 01:05:50 +0000
rtld: stop using unbound alloca()
PR: 295991
(cherry picked from commit 1e370f038778e16c4b31f8992dda339d429e5cb8)
---
libexec/rtld-elf/map_object.c | 4 ++-
libexec/rtld-elf/rtld.c | 82 ++++++++++++++++++++++++++++++-------------
libexec/rtld-elf/rtld.h | 2 ++
3 files changed, 63 insertions(+), 25 deletions(-)
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
index 6c6f0c9289b9..d769d01d3b24 100644
--- a/libexec/rtld-elf/map_object.c
+++ b/libexec/rtld-elf/map_object.c
@@ -113,7 +113,7 @@ map_object(int fd, const char *path, const struct stat *sb, bool ismain)
note_end = 0;
note_map = NULL;
note_map_len = 0;
- segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
+ segs = xcalloc(hdr->e_phnum, sizeof(segs[0]));
stack_flags = PF_X | PF_R | PF_W;
text_end = 0;
while (phdr < phlimit) {
@@ -341,6 +341,7 @@ map_object(int fd, const char *path, const struct stat *sb, bool ismain)
if (note_map != NULL)
munmap(note_map, note_map_len);
munmap(hdr, page_size);
+ free(segs);
return (obj);
error1:
@@ -351,6 +352,7 @@ error:
if (!phdr_in_zero_page(hdr))
munmap(phdr, hdr->e_phnum * sizeof(phdr[0]));
munmap(hdr, page_size);
+ free(segs);
return (NULL);
}
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 938e4b0b8a6b..864148df99ba 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -342,16 +342,6 @@ const char *ld_env_prefix = LD_;
static void (*rtld_exit_ptr)(void);
-/*
- * Fill in a DoneList with an allocation large enough to hold all of
- * the currently-loaded objects. Keep this as a macro since it calls
- * alloca and we want that to occur within the scope of the caller.
- */
-#define donelist_init(dlp) \
- ((dlp)->objs = alloca(obj_count * sizeof(dlp)->objs[0]), \
- assert((dlp)->objs != NULL), (dlp)->num_alloc = obj_count, \
- (dlp)->num_used = 0)
-
#define LD_UTRACE(e, h, mb, ms, r, n) \
do { \
if (ld_utrace != NULL) \
@@ -1042,6 +1032,38 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
return ((func_ptr_type)obj_main->entry);
}
+/*
+ * Fill in a DoneList with an allocation large enough to hold all of
+ * the currently-loaded Keep this as a macro since it calls
+ * 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 { \
+ DoneList *_dlp = _DLP; \
+ SymLook *_r = _REQ; \
+ _dlp->num_alloc = obj_count, \
+ _dlp->req = NULL; \
+ if (_dlp->num_alloc > DLP_ALLOCA_LIMIT) { \
+ _dlp->objs = xcalloc(_dlp->num_alloc, sizeof(_dlp->objs[0])); \
+ if (_r != NULL && _r->donelist_mem == NULL) { \
+ _r->donelist_mem = _dlp->objs; \
+ _dlp->req = _r; \
+ } \
+ } else { \
+ _dlp->objs = alloca(_dlp->num_alloc * sizeof(_dlp->objs[0])); \
+ } \
+ _dlp->num_used = 0; \
+} while (0)
+
+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;
+}
+
void *
rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
{
@@ -2319,7 +2341,7 @@ init_dag(Obj_Entry *root)
if (root->dag_inited)
return;
- donelist_init(&donelist);
+ donelist_init(&donelist, NULL);
/* Root object belongs to own DAG. */
objlist_push_tail(&root->dldags, root);
@@ -2342,6 +2364,7 @@ init_dag(Obj_Entry *root)
}
}
root->dag_inited = true;
+ donelist_free(&donelist);
}
static void
@@ -4103,8 +4126,11 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
LD_UTRACE(UTRACE_DLSYM_START, handle, NULL, 0, 0, name);
rlock_acquire(rtld_bind_lock, &lockstate);
- if (sigsetjmp(lockstate.env, 0) != 0)
+ if (sigsetjmp(lockstate.env, 0) != 0) {
lock_upgrade(rtld_bind_lock, &lockstate);
+ free(req.donelist_mem);
+ req.donelist_mem = NULL;
+ }
if (handle == NULL || handle == RTLD_NEXT || handle == RTLD_DEFAULT ||
handle == RTLD_SELF) {
if ((obj = obj_from_addr(retaddr)) == NULL) {
@@ -4173,7 +4199,7 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
return (NULL);
}
- donelist_init(&donelist);
+ donelist_init(&donelist, &req);
if (obj->mainprog) {
/* Handle obtained by dlopen(NULL, ...) implies global
* scope. */
@@ -4204,6 +4230,7 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
defobj = req.defobj_out;
}
}
+ donelist_free(&donelist);
}
if (def != NULL) {
@@ -4731,21 +4758,24 @@ get_program_var_addr(const char *name, RtldLockState *lockstate)
{
SymLook req;
DoneList donelist;
+ const void **res;
symlook_init(&req, name);
req.lockstate = lockstate;
- donelist_init(&donelist);
+ donelist_init(&donelist, NULL);
if (symlook_global(&req, &donelist) != 0)
return (NULL);
if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC)
- return ((const void **)make_function_pointer(req.sym_out,
- req.defobj_out));
+ res = (const void **)make_function_pointer(req.sym_out,
+ req.defobj_out);
else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC)
- return ((const void **)rtld_resolve_ifunc(req.defobj_out,
- req.sym_out));
+ res = (const void **)rtld_resolve_ifunc(req.defobj_out,
+ req.sym_out);
else
- return ((const void **)(req.defobj_out->relocbase +
- req.sym_out->st_value));
+ res = (const void **)(req.defobj_out->relocbase +
+ req.sym_out->st_value);
+ donelist_free(&donelist);
+ return (res);
}
/*
@@ -4820,7 +4850,7 @@ symlook_default(SymLook *req, const Obj_Entry *refobj)
SymLook req1;
int res;
- donelist_init(&donelist);
+ donelist_init(&donelist, req);
symlook_init_from_req(&req1, req);
/*
@@ -4873,6 +4903,7 @@ symlook_default(SymLook *req, const Obj_Entry *refobj)
}
}
+ donelist_free(&donelist);
return (req->sym_out != NULL ? 0 : ESRCH);
}
@@ -4954,13 +4985,15 @@ symlook_obj_load_filtees(SymLook *req, SymLook *req1, const Obj_Entry *obj,
Needed_Entry *needed)
{
DoneList donelist;
- int flags;
+ int flags, res;
flags = (req->flags & SYMLOOK_EARLY) != 0 ? RTLD_LO_EARLY : 0;
load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate);
- donelist_init(&donelist);
+ donelist_init(&donelist, NULL);
symlook_init_from_req(req1, req);
- return (symlook_needed(req1, needed, &donelist));
+ res = symlook_needed(req1, needed, &donelist);
+ donelist_free(&donelist);
+ return (res);
}
/*
@@ -6305,6 +6338,7 @@ 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 322d1ee94d6f..bdcfc3dc5ded 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -319,6 +319,7 @@ typedef struct Struct_SymCache {
*/
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 */
} DoneList;
@@ -348,6 +349,7 @@ typedef struct Struct_SymLook {
const Obj_Entry *defobj_out;
const Elf_Sym *sym_out;
struct Struct_RtldLockState *lockstate;
+ void *donelist_mem;
} SymLook;
enum {