mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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 commite74360e4ba)
This commit is contained in:
1 parent
4f7656fe8e
commit
1601fa5ac1
2 files changed
+64
-7
No files matched your search
+62
-7
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||