All of lore.kernel.org
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.com>
To: Song Liu <song@kernel.org>
Cc: live-patching@vger.kernel.org, jpoimboe@kernel.org,
	jikos@kernel.org, joe.lawrence@redhat.com,
	Miroslav Benes <mbenes@suse.cz>,
	Josh Poimboeuf <jpoimboe@redhat.com>
Subject: Re: [PATCH v7] livepatch: Clear relocation targets on a module removal
Date: Wed, 4 Jan 2023 11:26:04 +0100	[thread overview]
Message-ID: <Y7VUPAEFFFougaoC@alley> (raw)
In-Reply-To: <20221214174035.1012183-1-song@kernel.org>

On Wed 2022-12-14 09:40:35, Song Liu wrote:
> From: Miroslav Benes <mbenes@suse.cz>
> 
> Josh reported a bug:
> 
>   When the object to be patched is a module, and that module is
>   rmmod'ed and reloaded, it fails to load with:
> 
>   module: x86/modules: Skipping invalid relocation target, existing value is nonzero for type 2, loc 00000000ba0302e9, val ffffffffa03e293c
>   livepatch: failed to initialize patch 'livepatch_nfsd' for module 'nfsd' (-8)
>   livepatch: patch 'livepatch_nfsd' failed for module 'nfsd', refusing to load module 'nfsd'
> 
>   The livepatch module has a relocation which references a symbol
>   in the _previous_ loading of nfsd. When apply_relocate_add()
>   tries to replace the old relocation with a new one, it sees that
>   the previous one is nonzero and it errors out.
> 
> We thus decided to reverse the relocation patching (clear all relocation
> targets on x86_64). The solution is not
> universal and is too much arch-specific, but it may prove to be simpler
> in the end.
> 
> --- a/arch/powerpc/kernel/module_64.c
> +++ b/arch/powerpc/kernel/module_64.c
> @@ -739,6 +739,67 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
>  	return 0;
>  }
>  
> +#ifdef CONFIG_LIVEPATCH
> +void clear_relocate_add(Elf64_Shdr *sechdrs,
> +		       const char *strtab,
> +		       unsigned int symindex,
> +		       unsigned int relsec,
> +		       struct module *me)
> +{
> +	unsigned int i;
> +	Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
> +	Elf64_Sym *sym;
> +	unsigned long *location;
> +	const char *symname;
> +	u32 *instruction;
> +
> +	pr_debug("Clearing ADD relocate section %u to %u\n", relsec,
> +		 sechdrs[relsec].sh_info);
> +
> +	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
> +		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
> +			+ rela[i].r_offset;
> +		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
> +			+ ELF64_R_SYM(rela[i].r_info);
> +		symname = me->core_kallsyms.strtab
> +			+ sym->st_name;
> +
> +		if (ELF64_R_TYPE(rela[i].r_info) != R_PPC_REL24)
> +			continue;

Is it OK to continue?

IMHO, we should at least warn here. It means that the special elf
section contains a relocation that we are not able to clear. It will
most likely blow up when we try to load the livepatched module
again.

> +		/*
> +		 * reverse the operations in apply_relocate_add() for case
> +		 * R_PPC_REL24.
> +		 */
> +		if (sym->st_shndx != SHN_UNDEF &&
> +		    sym->st_shndx != SHN_LIVEPATCH)
> +			continue;

Same here. IMHO, we should warn when the section contains something
that we are not able to clear.

> +		/* skip mprofile and ftrace calls, same as restore_r2() */
> +		if (is_mprofile_ftrace_call(symname))
> +			continue;

Is this correct? restore_r2() returns "1" in this case. As a result
apply_relocate_add() returns immediately with -ENOEXEC. IMHO, we
should print a warning and return as well.

> +		instruction = (u32 *)location;
> +		/* skip sibling call, same as restore_r2() */
> +		if (!instr_is_relative_link_branch(ppc_inst(*instruction)))
> +			continue;

Same here. restore_r2() returns '1' in this case...

> +
> +		instruction += 1;
> +		/*
> +		 * Patch location + 1 back to NOP so the next
> +		 * apply_relocate_add() call (reload the module) will not
> +		 * fail the sanity check in restore_r2():
> +		 *
> +		 *         if (*instruction != PPC_RAW_NOP()) {
> +		 *             pr_err(...);
> +		 *             return 0;
> +		 *         }
> +		 */
> +		patch_instruction(instruction, ppc_inst(PPC_RAW_NOP()));
> +	}

This seems incomplete. The above code reverts patch_instruction() called
from restore_r2(). But there is another patch_instruction() called in
apply_relocate_add() for case R_PPC_REL24. IMHO, we should revert this
as well.

> +}
> +#endif

IMHO, this approach is really bad. The function is not maintainable.
It will be very hard to keep it in sync with apply_relocate_add().
And all the mistakes are just a proof.

IMHO, the only sane way is to avoid the code duplication.


>  #ifdef CONFIG_DYNAMIC_FTRACE
>  int module_trampoline_target(struct module *mod, unsigned long addr,
>  			     unsigned long *target)
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -261,6 +261,41 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
>  	return 0;
>  }
>  
> +static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
> +				    const char *shstrtab, const char *strtab,
> +				    unsigned int symndx, unsigned int secndx,
> +				    const char *objname, bool apply)
> +{
> +	int cnt, ret;
> +	char sec_objname[MODULE_NAME_LEN];
> +	Elf_Shdr *sec = sechdrs + secndx;
> +
> +	/*
> +	 * Format: .klp.rela.sec_objname.section_name
> +	 * See comment in klp_resolve_symbols() for an explanation
> +	 * of the selected field width value.
> +	 */
> +	cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]",
> +		     sec_objname);
> +	if (cnt != 1) {
> +		pr_err("section %s has an incorrectly formatted name\n",
> +		       shstrtab + sec->sh_name);
> +		return -EINVAL;
> +	}
> +
> +	if (strcmp(objname ? objname : "vmlinux", sec_objname))
> +		return 0;
> +
> +	ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, sec_objname);
> +	if (ret)
> +		return ret;

We do not need to call klp_resolve_symbols() when clearing the relocations.

> +
> +	if (apply)
> +		return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod);

Please, add an empty line here.

> +	clear_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
> +	return 0;
> +}
> +
>  /*
>   * At a high-level, there are two types of klp relocation sections: those which
>   * reference symbols which live in vmlinux; and those which reference symbols

Please, keep these comments above klp_write_section_relocs().
It is the function that implements these details and it is
called from more locations.

> @@ -289,31 +324,8 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
>  			     unsigned int symndx, unsigned int secndx,
>  			     const char *objname)
>  {
> -	int cnt, ret;
> -	char sec_objname[MODULE_NAME_LEN];
> -	Elf_Shdr *sec = sechdrs + secndx;
> -
> -	/*
> -	 * Format: .klp.rela.sec_objname.section_name
> -	 * See comment in klp_resolve_symbols() for an explanation
> -	 * of the selected field width value.
> -	 */
> -	cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]",
> -		     sec_objname);
> -	if (cnt != 1) {
> -		pr_err("section %s has an incorrectly formatted name\n",
> -		       shstrtab + sec->sh_name);
> -		return -EINVAL;
> -	}
> -
> -	if (strcmp(objname ? objname : "vmlinux", sec_objname))
> -		return 0;
> -
> -	ret = klp_resolve_symbols(sechdrs, strtab, symndx, sec, sec_objname);
> -	if (ret)
> -		return ret;
> -
> -	return apply_relocate_add(sechdrs, strtab, symndx, secndx, pmod);
> +	return klp_write_section_relocs(pmod, sechdrs, shstrtab, strtab, symndx,
> +					secndx, objname, true);
>  }
>  
>  /*

Best Regards,
Petr

  parent reply	other threads:[~2023-01-04 10:26 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-14 17:40 [PATCH v7] livepatch: Clear relocation targets on a module removal Song Liu
2023-01-03 17:00 ` Song Liu
2023-01-03 22:39   ` Joe Lawrence
2023-01-03 23:29     ` Song Liu
2023-01-04 10:26 ` Petr Mladek [this message]
2023-01-04 17:34   ` Song Liu
2023-01-04 23:12     ` Joe Lawrence
2023-01-05  5:59       ` Song Liu
2023-01-05 15:05         ` Joe Lawrence
2023-01-05 17:11           ` Song Liu
2023-01-06 13:02           ` Miroslav Benes
2023-01-06 16:26             ` Petr Mladek
2023-01-06 16:51               ` Song Liu
2023-01-05 11:19     ` Petr Mladek
2023-01-05 16:53       ` Song Liu
2023-01-05 18:09         ` Joe Lawrence
2023-01-05 13:03 ` Petr Mladek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y7VUPAEFFFougaoC@alley \
    --to=pmladek@suse.com \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=jpoimboe@redhat.com \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=song@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.