diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 236528bec6ebc83e9432c3479190062a4621a2a6..e19a91db9b351d3df8c3729390afb35e807040b5 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -591,6 +591,7 @@ static int ptrace_bts_clear(struct task_struct *child) } static int ptrace_bts_drain(struct task_struct *child, + long size, struct bts_struct __user *out) { int end, i; @@ -603,6 +604,9 @@ static int ptrace_bts_drain(struct task_struct *child, if (end <= 0) return end; + if (size < (end * sizeof(struct bts_struct))) + return -EIO; + for (i = 0; i < end; i++, out++) { struct bts_struct ret; int retval; @@ -617,7 +621,7 @@ static int ptrace_bts_drain(struct task_struct *child, ds_clear(ds); - return i; + return end; } static int ptrace_bts_realloc(struct task_struct *child, @@ -690,15 +694,22 @@ static int ptrace_bts_realloc(struct task_struct *child, } static int ptrace_bts_config(struct task_struct *child, + long cfg_size, const struct ptrace_bts_config __user *ucfg) { struct ptrace_bts_config cfg; int bts_size, ret = 0; void *ds; + if (cfg_size < sizeof(cfg)) + return -EIO; + if (copy_from_user(&cfg, ucfg, sizeof(cfg))) return -EFAULT; + if ((int)cfg.size < 0) + return -EINVAL; + bts_size = 0; ds = (void *)child->thread.ds_area_msr; if (ds) { @@ -734,6 +745,8 @@ static int ptrace_bts_config(struct task_struct *child, else clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); + ret = sizeof(cfg); + out: if (child->thread.debugctlmsr) set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); @@ -749,11 +762,15 @@ static int ptrace_bts_config(struct task_struct *child, } static int ptrace_bts_status(struct task_struct *child, + long cfg_size, struct ptrace_bts_config __user *ucfg) { void *ds = (void *)child->thread.ds_area_msr; struct ptrace_bts_config cfg; + if (cfg_size < sizeof(cfg)) + return -EIO; + memset(&cfg, 0, sizeof(cfg)); if (ds) { @@ -923,12 +940,12 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_BTS_CONFIG: ret = ptrace_bts_config - (child, (struct ptrace_bts_config __user *)addr); + (child, data, (struct ptrace_bts_config __user *)addr); break; case PTRACE_BTS_STATUS: ret = ptrace_bts_status - (child, (struct ptrace_bts_config __user *)addr); + (child, data, (struct ptrace_bts_config __user *)addr); break; case PTRACE_BTS_SIZE: @@ -946,7 +963,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_BTS_DRAIN: ret = ptrace_bts_drain - (child, (struct bts_struct __user *) addr); + (child, data, (struct bts_struct __user *) addr); break; default: diff --git a/include/asm-x86/ptrace-abi.h b/include/asm-x86/ptrace-abi.h index 32fe137822bf0ca57cabdaf05a6419b075415ba7..bcf67044754cd75d33716c064847936fe9dbd9da 100644 --- a/include/asm-x86/ptrace-abi.h +++ b/include/asm-x86/ptrace-abi.h @@ -99,13 +99,15 @@ struct ptrace_bts_config { #define PTRACE_BTS_CONFIG 40 /* Configure branch trace recording. - DATA is ignored, ADDR points to a struct ptrace_bts_config. + ADDR points to a struct ptrace_bts_config. + DATA gives the size of that buffer. A new buffer is allocated, iff the size changes. + Returns the number of bytes read. */ #define PTRACE_BTS_STATUS 41 -/* Return the current configuration. - DATA is ignored, ADDR points to a struct ptrace_bts_config - that will contain the result. +/* Return the current configuration in a struct ptrace_bts_config + pointed to by ADDR; DATA gives the size of that buffer. + Returns the number of bytes written. */ #define PTRACE_BTS_SIZE 42 /* Return the number of available BTS records. @@ -123,8 +125,8 @@ struct ptrace_bts_config { */ #define PTRACE_BTS_DRAIN 45 /* Read all available BTS records and clear the buffer. - DATA is ignored. ADDR points to an array of struct bts_struct of - suitable size. + ADDR points to an array of struct bts_struct. + DATA gives the size of that buffer. BTS records are read from oldest to newest. Returns number of BTS records drained. */