Эх сурвалжийг харах

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: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Markus Metzger 17 жил өмнө
parent
commit
cba4b65d35

+ 21 - 4
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,
 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,
 	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,
 
 
 	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 @@ out:
 }
 }
 
 
 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,
 	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 @@ errout:
 }
 }
 
 
 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)
 
 
 	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)
 
 
 	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:

+ 8 - 6
include/asm-x86/ptrace-abi.h

@@ -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.
-   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
 #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 {
 */
 */
 #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
-   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.
    BTS records are read from oldest to newest.
    Returns number of BTS records drained.
    Returns number of BTS records drained.
 */
 */