xen/livepatch: Fix .altinstructions safety checks

The prior check has && vs || mixups, making it tautologically false and thus
providing no safety at all.  There are boundary errors too.

First start with a comment describing how the .altinstructions and
.altinstr_replacement sections interact, and perform suitable cross-checking.

Second, rewrite the alt_instr loop entirely from scratch.  Origin sites have
non-zero size, and must be fully contained within the livepatches .text
section(s).  Any non-zero sized replacements must be fully contained within
the .altinstr_replacement section.

Fixes: f8a10174e8 ("xsplice: Add support for alternatives")
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Ross Lagerwall <ross.lagerwall@citrix.com>
(cherry picked from commit e74360e4ba)
This commit is contained in:
Andrew Cooper committed 2024-04-09 12:56:55 +01:00
1 parent 4f7656fe8e
commit 1601fa5ac1
2 files changed
+64 -7

No files matched your search

+62 -7
View File
@@ -816,29 +816,84 @@ static int prepare_payload(struct payload *payload,
if ( sec )
{
#ifdef CONFIG_HAS_ALTERNATIVE
/*
* (As of April 2023), Alternatives are formed of:
* - An .altinstructions section with an array of struct alt_instr's.
* - An .altinstr_replacement section containing instructions.
*
* An individual alt_instr contains:
* - An orig reference, pointing into .text with a nonzero length
* - A repl reference, pointing into .altinstr_replacement
*
* It is legal to have zero-length replacements, meaning it is legal
* for the .altinstr_replacement section to be empty too. An
* implementation detail means that a zero-length replacement's repl
* reference will still be in the .altinstr_replacement section.
*/
const struct livepatch_elf_sec *repl_sec;
struct alt_instr *a, *start, *end;
if ( !section_ok(elf, sec, sizeof(*a)) )
return -EINVAL;
/* Tolerate an empty .altinstructions section... */
if ( sec->sec->sh_size == 0 )
goto alt_done;
/* ... but otherwise, there needs to be something to alter... */
if ( payload->text_size == 0 )
{
printk(XENLOG_ERR LIVEPATCH "%s Alternatives provided, but no .text\n",
elf->name);
return -EINVAL;
}
/* ... and something to be altered to. */
repl_sec = livepatch_elf_sec_by_name(elf, ".altinstr_replacement");
if ( !repl_sec )
{
printk(XENLOG_ERR LIVEPATCH "%s .altinstructions provided, but no .altinstr_replacement\n",
elf->name);
return -EINVAL;
}
start = sec->load_addr;
end = sec->load_addr + sec->sec->sh_size;
for ( a = start; a < end; a++ )
{
const void *instr = ALT_ORIG_PTR(a);
const void *replacement = ALT_REPL_PTR(a);
const void *orig = ALT_ORIG_PTR(a);
const void *repl = ALT_REPL_PTR(a);
if ( (instr < region->text_start && instr >= region->text_end) ||
(replacement < region->text_start &&
replacement >= region->text_end) )
/* orig must be fully within .text. */
if ( orig < payload->text_addr ||
a->orig_len > payload->text_size ||
orig + a->orig_len > payload->text_addr + payload->text_size )
{
printk(XENLOG_ERR LIVEPATCH "%s Alt patching outside payload: %p\n",
elf->name, instr);
printk(XENLOG_ERR LIVEPATCH
"%s Alternative orig %p+%#x outside payload text %p+%#zx\n",
elf->name, orig, a->orig_len,
payload->text_addr, payload->text_size);
return -EINVAL;
}
/*
* repl must be fully within .altinstr_replacement, even if the
* replacement and the section happen to both have zero length.
*/
if ( repl < repl_sec->load_addr ||
a->repl_len > repl_sec->sec->sh_size ||
repl + a->repl_len > repl_sec->load_addr + repl_sec->sec->sh_size )
{
printk(XENLOG_ERR LIVEPATCH
"%s Alternative repl %p+%#x outside .altinstr_replacement %p+%#"PRIxElfWord"\n",
elf->name, repl, a->repl_len,
repl_sec->load_addr, repl_sec->sec->sh_size);
return -EINVAL;
}
}
apply_alternatives(start, end);
alt_done:;
#else
printk(XENLOG_ERR LIVEPATCH "%s: We don't support alternative patching\n",
elf->name);
+2
View File
@@ -563,6 +563,7 @@ typedef struct {
#if defined(ELFSIZE) && (ELFSIZE == 32)
#define PRIxElfAddr PRIx32
#define PRIuElfWord PRIu32
#define PRIxElfWord PRIx32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Phdr Elf32_Phdr
@@ -591,6 +592,7 @@ typedef struct {
#elif defined(ELFSIZE) && (ELFSIZE == 64)
#define PRIxElfAddr PRIx64
#define PRIuElfWord PRIu64
#define PRIxElfWord PRIx64
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Phdr Elf64_Phdr
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.