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

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
Loading
Loading
Loading
Loading
+21 −4
Original line number Diff line number Diff line
@@ -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 @@ out:
}

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 @@ errout:
}

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:
+8 −6
Original line number Diff line number Diff line
@@ -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.
*/