Hi Farid, On Sun, Jun 21, 2026 at 09:39:33PM -0700, Farid Zakaria wrote: > Currently, standard ELF and ELF FDPIC loaders expect a fixed path to the > dynamic linker/interpreter (PT_INTERP). However, for systems utilizing > relocatable dynamic interpreters (such as Nix/store-based environments), > hardcoding this path is inflexible and breaks binary portability. > > Introduce support for resolving the $ORIGIN placeholder in the ELF > interpreter path. This maps the dynamic linker relative to the path > of the binary being executed, matching user-space origin resolution. > > To avoid code duplication, implement a shared 'resolve_elf_interpreter()' > helper in the VFS exec layer. For safety, limit detection strictly to > the prefix string "$ORIGIN" to prevent complex parsing exploits. > > Assisted-by: Antigravity:Gemini-Pro
This isn't a requirement from the community or anything, but I always find it useful if I see an Assisted-by tag to know what assistence was actually delivered by an LLM. Otherwise we might as well add assisted-by tags for any editor. Talking about LLMs, your patch has some issues flagged by Sashiko[1]. Please take a look. > Signed-off-by: Farid Zakaria <[email protected]> > --- > fs/binfmt_elf.c | 11 +++++++++-- > fs/binfmt_elf_fdpic.c | 15 +++++++++++++-- > fs/exec.c | 42 +++++++++++++++++++++++++++++++++++++++++ > include/linux/binfmts.h | 2 ++ > 4 files changed, 66 insertions(+), 4 deletions(-) > > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index 16a56b6b3..af11f96ae 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -872,7 +872,7 @@ static int load_elf_binary(struct linux_binprm *bprm) > > elf_ppnt = elf_phdata; > for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) { > - char *elf_interpreter; > + char *elf_interpreter, *resolved_interp; > > if (elf_ppnt->p_type == PT_GNU_PROPERTY) { > elf_property_phdata = elf_ppnt; > @@ -904,8 +904,15 @@ static int load_elf_binary(struct linux_binprm *bprm) > if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0') > goto out_free_interp; > > - interpreter = open_exec(elf_interpreter); > + resolved_interp = resolve_elf_interpreter(bprm, > elf_interpreter); > kfree(elf_interpreter); > + if (IS_ERR(resolved_interp)) { > + retval = PTR_ERR(resolved_interp); > + goto out_free_ph; > + } > + > + interpreter = open_exec(resolved_interp); > + kfree(resolved_interp); > retval = PTR_ERR(interpreter); > if (IS_ERR(interpreter)) > goto out_free_ph; > diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c > index 7e3108489..e85727d71 100644 > --- a/fs/binfmt_elf_fdpic.c > +++ b/fs/binfmt_elf_fdpic.c > @@ -230,7 +230,9 @@ static int load_elf_fdpic_binary(struct linux_binprm > *bprm) > > for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { > switch (phdr->p_type) { > - case PT_INTERP: > + case PT_INTERP: { > + char *resolved_interp; > + > retval = -ENOMEM; > if (phdr->p_filesz > PATH_MAX) > goto error; > @@ -259,7 +261,15 @@ static int load_elf_fdpic_binary(struct linux_binprm > *bprm) > kdebug("Using ELF interpreter %s", interpreter_name); > > /* replace the program with the interpreter */ > - interpreter = open_exec(interpreter_name); > + resolved_interp = resolve_elf_interpreter(bprm, > interpreter_name); > + kfree(interpreter_name); > + if (IS_ERR(resolved_interp)) { > + retval = PTR_ERR(resolved_interp); > + goto error; > + } > + > + interpreter = open_exec(resolved_interp); > + kfree(resolved_interp); > retval = PTR_ERR(interpreter); > if (IS_ERR(interpreter)) { > interpreter = NULL; > @@ -284,6 +294,7 @@ static int load_elf_fdpic_binary(struct linux_binprm > *bprm) > > interp_params.hdr = *((struct elfhdr *) bprm->buf); > break; > + } > > case PT_LOAD: > #ifdef CONFIG_MMU > diff --git a/fs/exec.c b/fs/exec.c > index b92fe7db1..0978ae613 100644 > --- a/fs/exec.c > +++ b/fs/exec.c > @@ -2024,6 +2024,48 @@ static int __init init_fs_exec_sysctls(void) > fs_initcall(init_fs_exec_sysctls); > #endif /* CONFIG_SYSCTL */ > > +char *resolve_elf_interpreter(struct linux_binprm *bprm, const char > *elf_interpreter) > +{ > + char *pathbuf, *path, *slash, *resolved; > + > + if (strncmp(elf_interpreter, "$ORIGIN", 7) != 0) { > + char *ret = kstrdup(elf_interpreter, GFP_KERNEL); > + > + return ret ? ret : ERR_PTR(-ENOMEM); > + } > + > + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); > + if (!pathbuf) > + return ERR_PTR(-ENOMEM); > + > + path = file_path(bprm->file, pathbuf, PATH_MAX); > + if (IS_ERR(path)) { > + kfree(pathbuf); > + return (char *)path; > + } > + > + slash = strrchr(path, '/'); > + if (slash) { > + if (slash == path) > + *(slash + 1) = '\0'; > + else > + *slash = '\0'; > + } else { > + kfree(pathbuf); > + char *ret = kstrdup(elf_interpreter, GFP_KERNEL); > + > + return ret ? ret : ERR_PTR(-ENOMEM); > + } > + > + resolved = kasprintf(GFP_KERNEL, "%s%s", path, elf_interpreter + 7); > + kfree(pathbuf); > + if (!resolved) > + return ERR_PTR(-ENOMEM); > + > + return resolved; > +} > +EXPORT_SYMBOL(resolve_elf_interpreter); > + > #ifdef CONFIG_EXEC_KUNIT_TEST > #include "tests/exec_kunit.c" > #endif > diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h > index 2c77e383e..17419cd3d 100644 > --- a/include/linux/binfmts.h > +++ b/include/linux/binfmts.h > @@ -150,4 +150,6 @@ extern ssize_t read_code(struct file *, unsigned long, > loff_t, size_t); > int kernel_execve(const char *filename, > const char *const *argv, const char *const *envp); > > +char *resolve_elf_interpreter(struct linux_binprm *bprm, const char > *elf_interpreter); > + > #endif /* _LINUX_BINFMTS_H */ > -- > 2.51.2 > Thanks, Jori. [1]: https://sashiko.dev/#/patchset/20260622043934.179879-1-farid.m.zakaria%40gmail.com

