Hi Lukas,

As mentioned offlist, reviewing and testing this is on my TODO list, but
here are some early notes ...

On Thu, May 16, 2024 at 03:30:05PM +0200, Lukas Hruska wrote:
> Livepatches need to access external symbols which can't be handled
> by the normal relocation mechanism. It is needed for two types
> of symbols:
> 
>   + Symbols which can be local for the original livepatched function.
>     The alternative implementation in the livepatch sees them
>     as external symbols.
> 
>   + Symbols in modules which are exported via EXPORT_SYMBOL*(). They
>     must be handled special way otherwise the livepatch module would
>     depend on the livepatched one. Loading such livepatch would cause
>     loading the other module as well.
> 
> The address of these symbols can be found via kallsyms. Or they can
> be relocated using livepatch specific relocation sections as specified
> in Documentation/livepatch/module-elf-format.txt.
> 
> Currently, there is no trivial way to embed the required information as
> requested in the final livepatch elf object. klp-convert solves this
> problem by using annotations in the elf object to convert the relocation
> accordingly to the specification, enabling it to be handled by the
> livepatch loader.
> 
> Given the above, create scripts/livepatch to hold tools developed for
> livepatches and add source files for klp-convert there.
> 
> Allow to annotate such external symbols in the livepatch by a macro
> KLP_RELOC_SYMBOL(). It will create symbol with all needed
> metadata. For example:
> 
>   extern char *saved_command_line \
>                  KLP_RELOC_SYMBOL(vmlinux, vmlinux, saved_command_line, 0);

Nit: should this be KLP_RELOC_SYMBOL_POS if it including the 0 position?
(Or KLP_RELOC_SYMBOL and omit the implied 0-th position.)

> diff --git a/scripts/livepatch/elf.c b/scripts/livepatch/elf.c
> --- /dev/null
> +++ b/scripts/livepatch/elf.c
> +static int update_shstrtab(struct elf *elf)
> +{
> +     struct section *shstrtab, *sec;
> +     size_t orig_size, new_size = 0, offset, len;
> +     char *buf;
> +
> +     shstrtab = find_section_by_name(elf, ".shstrtab");
> +     if (!shstrtab) {
> +             WARN("can't find .shstrtab");
> +             return -1;
> +     }
> +
> +     orig_size = new_size = shstrtab->size;
> +
> +     list_for_each_entry(sec, &elf->sections, list) {
> +             if (sec->sh.sh_name != ~0U)
> +                     continue;
> +             new_size += strlen(sec->name) + 1;
> +     }
> +
> +     if (new_size == orig_size)
> +             return 0;
> +
> +     buf = malloc(new_size);
> +     if (!buf) {
> +             WARN("malloc failed");
> +             return -1;
> +     }
> +     memcpy(buf, (void *)shstrtab->data, orig_size);

While reviewing klp-convert v7 [1], Alexey Dobriyan notes here,

"This code is called realloc(). :-)"

[1] 
https://lore.kernel.org/live-patching/4ce29654-4e1e-4680-9c25-715823ff5e02@p183/

> +static int update_strtab(struct elf *elf)
> +{
> +     struct section *strtab;
> +     struct symbol *sym;
> +     size_t orig_size, new_size = 0, offset, len;
> +     char *buf;
> +
> +     strtab = find_section_by_name(elf, ".strtab");
> +     if (!strtab) {
> +             WARN("can't find .strtab");
> +             return -1;
> +     }
> +
> +     orig_size = new_size = strtab->size;
> +
> +     list_for_each_entry(sym, &elf->symbols, list) {
> +             if (sym->sym.st_name != ~0U)
> +                     continue;
> +             new_size += strlen(sym->name) + 1;
> +     }
> +
> +     if (new_size == orig_size)
> +             return 0;
> +
> +     buf = malloc(new_size);
> +     if (!buf) {
> +             WARN("malloc failed");
> +             return -1;
> +     }
> +     memcpy(buf, (void *)strtab->data, orig_size);

I think Alexey's realloc suggestion would apply here, too.

> +static int write_file(struct elf *elf, const char *file)
> +{
> +     int fd;
> +     Elf *e;
> +     GElf_Ehdr eh, ehout;
> +     Elf_Scn *scn;
> +     Elf_Data *data;
> +     GElf_Shdr sh;
> +     struct section *sec;
> +
> +     fd = creat(file, 0664);
> +     if (fd == -1) {
> +             WARN("couldn't create %s", file);
> +             return -1;
> +     }
> +
> +     e = elf_begin(fd, ELF_C_WRITE, NULL);

Alexy also found an ELF coding bug:

"elf_end() doesn't close descriptor, so there is potentially corrupted
data. There is no unlink() call if writes fail as well."

> +void elf_close(struct elf *elf)
> +{
> +     struct section *sec, *tmpsec;
> +     struct symbol *sym, *tmpsym;
> +     struct rela *rela, *tmprela;
> +
> +     list_for_each_entry_safe(sym, tmpsym, &elf->symbols, list) {
> +             list_del(&sym->list);
> +             free(sym);
> +     }
> +     list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
> +             list_for_each_entry_safe(rela, tmprela, &sec->relas, list) {
> +                     list_del(&rela->list);
> +                     free(rela);
> +             }
> +             list_del(&sec->list);
> +             free(sec);
> +     }
> +     if (elf->fd > 0)
> +             close(elf->fd);

Alexy found another ELF coding bug here:

"Techically, it is "fd >= 0"."


I had coded fixes for these in a v8-devel that I never finished.  It
shouldn't be too hard to fix these up in the minimal version of the
patchset, but lmk if you'd like a patch.

That's all for now.  My plan is to try and turn off kpatch-build's
klp-relocation code and see how passing through to klp-convert fares.
That would give us a good comparison of real-world examples that need to
be handled and tested.

--
Joe


Reply via email to