Again, checks aren't enough on all counts. Signed-off-by: Alexey Dobriyan <adobriyan@xxxxxxxxx> --- include/linux/kstate-image.h | 3 ++ kernel/kstate/kstate-x86_64.c | 44 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/include/linux/kstate-image.h b/include/linux/kstate-image.h index 7c54711..d956d05 100644 --- a/include/linux/kstate-image.h +++ b/include/linux/kstate-image.h @@ -151,6 +151,9 @@ struct kstate_image_task_struct_x86_64 { __u64 dr7; __u64 tls_array[3]; + + __u8 len_xstate; + /* __u8 xstate[len_xstate]; */ } __packed; struct kstate_image_mm_struct { diff --git a/kernel/kstate/kstate-x86_64.c b/kernel/kstate/kstate-x86_64.c index 83ed1b5..e18cca1 100644 --- a/kernel/kstate/kstate-x86_64.c +++ b/kernel/kstate/kstate-x86_64.c @@ -1,5 +1,6 @@ /* Copyright (C) 2000-2009 Parallels Holdings, Ltd. */ #include <linux/sched.h> +#include <asm/i387.h> #include <linux/kstate.h> #include <linux/kstate-image.h> @@ -74,10 +75,14 @@ static int check_tls(struct desc_struct *desc) static int check_image_task_struct_x86_64(struct kstate_image_task_struct *tsk_i) { struct kstate_image_task_struct_x86_64 *i = (void *)(tsk_i + 1); + unsigned int len_xstate; int rv; if (tsk_i->hdr.obj_len < sizeof(*tsk_i) + sizeof(*i)) return -EINVAL; + len_xstate = i->len_xstate; + if (tsk_i->hdr.obj_len - sizeof(*tsk_i) - sizeof(*i) < len_xstate) + return -EINVAL; rv = check_rflags(i->rflags); if (rv < 0) @@ -129,6 +134,11 @@ static int check_image_task_struct_x86_64(struct kstate_image_task_struct *tsk_i return rv; } + if (len_xstate > 0 && len_xstate != xstate_size) { + WARN(1, "xstate size mismatch %u:%u\n", len_xstate, xstate_size); + return -EINVAL; + } + return 0; } @@ -141,7 +151,12 @@ int kstate_arch_check_image_task_struct(struct kstate_image_task_struct *i) unsigned int kstate_arch_len_task_struct(struct task_struct *tsk) { - return sizeof(struct kstate_image_task_struct_x86_64); + unsigned int len; + + len = sizeof(struct kstate_image_task_struct_x86_64); + if (tsk->thread.xstate) + len += xstate_size; + return len; } int kstate_arch_check_task_struct(struct task_struct *tsk) @@ -154,10 +169,6 @@ int kstate_arch_check_task_struct(struct task_struct *tsk) return -EINVAL; } #endif - if (tsk->thread.xstate) { - WARN_ON(1); - return -EINVAL; - } rb = &task_thread_info(tsk)->restart_block; if (rb->fn != current_thread_info()->restart_block.fn) { WARN(1, "rb->fn = %pF\n", rb->fn); @@ -261,6 +272,12 @@ static int dump_task_struct_x86_64(struct kstate_context *ctx, struct task_struc BUILD_BUG_ON(sizeof(tsk->thread.tls_array) != 3 * 8); memcpy(i->tls_array, tsk->thread.tls_array, sizeof(i->tls_array)); + i->len_xstate = 0; + if (tsk->thread.xstate) { + i->len_xstate = xstate_size; + memcpy(i + 1, tsk->thread.xstate, xstate_size); + } + return 0; } @@ -269,9 +286,20 @@ int kstate_arch_dump_task_struct(struct kstate_context *ctx, struct task_struct return dump_task_struct_x86_64(ctx, tsk, arch_i); } +static int restore_xstate(struct task_struct *tsk, void *xstate, unsigned int len) +{ + int rv; + + rv = init_fpu(tsk); + if (rv == 0) + memcpy(tsk->thread.xstate, xstate, len); + return rv; +} + static int restore_task_struct_x86_64(struct task_struct *tsk, struct kstate_image_task_struct_x86_64 *i) { struct pt_regs *regs = task_pt_regs(tsk); + int rv; tsk->thread.sp = (unsigned long)regs; tsk->thread.sp0 = (unsigned long)(regs + 1); @@ -319,6 +347,12 @@ static int restore_task_struct_x86_64(struct task_struct *tsk, struct kstate_ima memcpy(tsk->thread.tls_array, i->tls_array, sizeof(i->tls_array)); + if (i->len_xstate) { + rv = restore_xstate(tsk, i + 1, i->len_xstate); + if (rv < 0) + return rv; + } + set_tsk_thread_flag(tsk, TIF_FORK); return 0; } -- 1.5.6.5 _______________________________________________ Containers mailing list Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/containers