Commit cba4b65d authored by Markus Metzger's avatar Markus Metzger Committed by Ingo Molnar

x86, ptrace: add buffer size checks

Pass the buffer size for (most) ptrace commands that pass user-allocated buffers and check that size before accessing the buffer. Unfortunately, PTRACE_BTS_GET already uses all 4 parameters.
Commands that access user buffers return the number of bytes or records read or written.
Signed-off-by: default avatarMarkus Metzger <markus.t.metzger@intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent e6ae5d95
...@@ -591,6 +591,7 @@ static int ptrace_bts_clear(struct task_struct *child) ...@@ -591,6 +591,7 @@ static int ptrace_bts_clear(struct task_struct *child)
} }
static int ptrace_bts_drain(struct task_struct *child, static int ptrace_bts_drain(struct task_struct *child,
long size,
struct bts_struct __user *out) struct bts_struct __user *out)
{ {
int end, i; int end, i;
...@@ -603,6 +604,9 @@ static int ptrace_bts_drain(struct task_struct *child, ...@@ -603,6 +604,9 @@ static int ptrace_bts_drain(struct task_struct *child,
if (end <= 0) if (end <= 0)
return end; return end;
if (size < (end * sizeof(struct bts_struct)))
return -EIO;
for (i = 0; i < end; i++, out++) { for (i = 0; i < end; i++, out++) {
struct bts_struct ret; struct bts_struct ret;
int retval; int retval;
...@@ -617,7 +621,7 @@ static int ptrace_bts_drain(struct task_struct *child, ...@@ -617,7 +621,7 @@ static int ptrace_bts_drain(struct task_struct *child,
ds_clear(ds); ds_clear(ds);
return i; return end;
} }
static int ptrace_bts_realloc(struct task_struct *child, static int ptrace_bts_realloc(struct task_struct *child,
...@@ -690,15 +694,22 @@ 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, static int ptrace_bts_config(struct task_struct *child,
long cfg_size,
const struct ptrace_bts_config __user *ucfg) const struct ptrace_bts_config __user *ucfg)
{ {
struct ptrace_bts_config cfg; struct ptrace_bts_config cfg;
int bts_size, ret = 0; int bts_size, ret = 0;
void *ds; void *ds;
if (cfg_size < sizeof(cfg))
return -EIO;
if (copy_from_user(&cfg, ucfg, sizeof(cfg))) if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
return -EFAULT; return -EFAULT;
if ((int)cfg.size < 0)
return -EINVAL;
bts_size = 0; bts_size = 0;
ds = (void *)child->thread.ds_area_msr; ds = (void *)child->thread.ds_area_msr;
if (ds) { if (ds) {
...@@ -734,6 +745,8 @@ static int ptrace_bts_config(struct task_struct *child, ...@@ -734,6 +745,8 @@ static int ptrace_bts_config(struct task_struct *child,
else else
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
ret = sizeof(cfg);
out: out:
if (child->thread.debugctlmsr) if (child->thread.debugctlmsr)
set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
...@@ -749,11 +762,15 @@ static int ptrace_bts_config(struct task_struct *child, ...@@ -749,11 +762,15 @@ static int ptrace_bts_config(struct task_struct *child,
} }
static int ptrace_bts_status(struct task_struct *child, static int ptrace_bts_status(struct task_struct *child,
long cfg_size,
struct ptrace_bts_config __user *ucfg) struct ptrace_bts_config __user *ucfg)
{ {
void *ds = (void *)child->thread.ds_area_msr; void *ds = (void *)child->thread.ds_area_msr;
struct ptrace_bts_config cfg; struct ptrace_bts_config cfg;
if (cfg_size < sizeof(cfg))
return -EIO;
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
if (ds) { if (ds) {
...@@ -923,12 +940,12 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -923,12 +940,12 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
case PTRACE_BTS_CONFIG: case PTRACE_BTS_CONFIG:
ret = ptrace_bts_config ret = ptrace_bts_config
(child, (struct ptrace_bts_config __user *)addr); (child, data, (struct ptrace_bts_config __user *)addr);
break; break;
case PTRACE_BTS_STATUS: case PTRACE_BTS_STATUS:
ret = ptrace_bts_status ret = ptrace_bts_status
(child, (struct ptrace_bts_config __user *)addr); (child, data, (struct ptrace_bts_config __user *)addr);
break; break;
case PTRACE_BTS_SIZE: case PTRACE_BTS_SIZE:
...@@ -946,7 +963,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -946,7 +963,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
case PTRACE_BTS_DRAIN: case PTRACE_BTS_DRAIN:
ret = ptrace_bts_drain ret = ptrace_bts_drain
(child, (struct bts_struct __user *) addr); (child, data, (struct bts_struct __user *) addr);
break; break;
default: default:
......
...@@ -99,13 +99,15 @@ struct ptrace_bts_config { ...@@ -99,13 +99,15 @@ struct ptrace_bts_config {
#define PTRACE_BTS_CONFIG 40 #define PTRACE_BTS_CONFIG 40
/* Configure branch trace recording. /* 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. A new buffer is allocated, iff the size changes.
Returns the number of bytes read.
*/ */
#define PTRACE_BTS_STATUS 41 #define PTRACE_BTS_STATUS 41
/* Return the current configuration. /* Return the current configuration in a struct ptrace_bts_config
DATA is ignored, ADDR points to a struct ptrace_bts_config pointed to by ADDR; DATA gives the size of that buffer.
that will contain the result. Returns the number of bytes written.
*/ */
#define PTRACE_BTS_SIZE 42 #define PTRACE_BTS_SIZE 42
/* Return the number of available BTS records. /* Return the number of available BTS records.
...@@ -123,8 +125,8 @@ struct ptrace_bts_config { ...@@ -123,8 +125,8 @@ struct ptrace_bts_config {
*/ */
#define PTRACE_BTS_DRAIN 45 #define PTRACE_BTS_DRAIN 45
/* Read all available BTS records and clear the buffer. /* Read all available BTS records and clear the buffer.
DATA is ignored. ADDR points to an array of struct bts_struct of ADDR points to an array of struct bts_struct.
suitable size. DATA gives the size of that buffer.
BTS records are read from oldest to newest. BTS records are read from oldest to newest.
Returns number of BTS records drained. Returns number of BTS records drained.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment