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

Reply via email to