That said we should provide hook functions so that the existing data
can be changed during payload application.
+To guarantee safety we disallow re-applying an payload after it has been
+reverted. This is because we cannot guarantee that the state of .bss
+and .data to be exactly as it was during loading. Hence the administrator
+MUST unload the payload and upload it again to apply it.
+
+There is an exception to this: if the payload only has .livepatch.funcs;
+and the .data or .bss sections are of zero length.
### Inline patching
struct payload {
uint32_t state; /* One of the LIVEPATCH_STATE_*. */
int32_t rc; /* 0 or -XEN_EXX. */
+ bool reverted; /* Whether it was reverted. */
+ bool safe_to_reapply; /* Can apply safely after revert. */
struct list_head list; /* Linked to 'payload_list'. */
const void *text_addr; /* Virtual address of .text. */
size_t text_size; /* .. and its size. */
static int move_payload(struct payload *payload, struct livepatch_elf *elf)
{
void *text_buf, *ro_buf, *rw_buf;
- unsigned int i;
+ unsigned int i, rw_buf_sec, rw_buf_cnt = 0;
size_t size = 0;
unsigned int *offset;
int rc = 0;
* and .shstrtab. For the non-relocate we allocate and copy these
* via other means - and the .rel we can ignore as we only use it
* once during loading.
+ *
+ * Also ignore sections with zero size. Those can be for example:
+ * data, or .bss.
*/
- if ( !(elf->sec[i].sec->sh_flags & SHF_ALLOC) )
+ if ( livepatch_elf_ignore_section(elf->sec[i].sec) )
offset[i] = UINT_MAX;
else if ( (elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
!(elf->sec[i].sec->sh_flags & SHF_WRITE) )
for ( i = 1; i < elf->hdr->e_shnum; i++ )
{
- if ( elf->sec[i].sec->sh_flags & SHF_ALLOC )
+ if ( !livepatch_elf_ignore_section(elf->sec[i].sec) )
{
void *buf;
if ( elf->sec[i].sec->sh_flags & SHF_EXECINSTR )
buf = text_buf;
else if ( elf->sec[i].sec->sh_flags & SHF_WRITE )
+ {
buf = rw_buf;
+ rw_buf_sec = i;
+ rw_buf_cnt++;
+ }
else
buf = ro_buf;
}
}
+ /* Only one RW section with non-zero size: .livepatch.funcs */
+ if ( rw_buf_cnt == 1 &&
+ !strcmp(elf->sec[rw_buf_sec].name, ELF_LIVEPATCH_FUNC) )
+ payload->safe_to_reapply = true;
out:
xfree(offset);
list_del_rcu(&data->applied_list);
unregister_virtual_region(&data->region);
+ data->reverted = true;
return 0;
}
case LIVEPATCH_ACTION_APPLY:
if ( data->state == LIVEPATCH_STATE_CHECKED )
{
+ /*
+ * It is unsafe to apply an reverted payload as the .data (or .bss)
+ * may not be in in pristine condition. Hence MUST unload and then
+ * apply patch again. Unless the payload has only one
+ * RW section (.livepatch.funcs).
+ */
+ if ( data->reverted && !data->safe_to_reapply )
+ {
+ dprintk(XENLOG_ERR, "%s%s: can't revert as payload has .data. Please unload!\n",
+ LIVEPATCH, data->name);
+ data->rc = -EINVAL;
+ break;
+ }
+
rc = build_id_dep(data, !!list_empty(&applied_list));
if ( rc )
break;