diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2008-01-30 07:32:03 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:32:03 -0500 |
commit | cba4b65d359268c40679ca75ac92c0b93cecf6de (patch) | |
tree | 95352d42a1a51338b08b88603cd4678297d56917 | |
parent | e6ae5d9540727b0e2e5e2fbeb683c84671ed0a31 (diff) |
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>
-rw-r--r-- | arch/x86/kernel/ptrace.c | 25 | ||||
-rw-r--r-- | include/asm-x86/ptrace-abi.h | 14 |
2 files changed, 29 insertions, 10 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 236528bec6eb..e19a91db9b35 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) | |||
591 | } | 591 | } |
592 | 592 | ||
593 | static int ptrace_bts_drain(struct task_struct *child, | 593 | static int ptrace_bts_drain(struct task_struct *child, |
594 | long size, | ||
594 | struct bts_struct __user *out) | 595 | struct bts_struct __user *out) |
595 | { | 596 | { |
596 | int end, i; | 597 | int end, i; |
@@ -603,6 +604,9 @@ static int ptrace_bts_drain(struct task_struct *child, | |||
603 | if (end <= 0) | 604 | if (end <= 0) |
604 | return end; | 605 | return end; |
605 | 606 | ||
607 | if (size < (end * sizeof(struct bts_struct))) | ||
608 | return -EIO; | ||
609 | |||
606 | for (i = 0; i < end; i++, out++) { | 610 | for (i = 0; i < end; i++, out++) { |
607 | struct bts_struct ret; | 611 | struct bts_struct ret; |
608 | int retval; | 612 | int retval; |
@@ -617,7 +621,7 @@ static int ptrace_bts_drain(struct task_struct *child, | |||
617 | 621 | ||
618 | ds_clear(ds); | 622 | ds_clear(ds); |
619 | 623 | ||
620 | return i; | 624 | return end; |
621 | } | 625 | } |
622 | 626 | ||
623 | static int ptrace_bts_realloc(struct task_struct *child, | 627 | static int ptrace_bts_realloc(struct task_struct *child, |
@@ -690,15 +694,22 @@ out: | |||
690 | } | 694 | } |
691 | 695 | ||
692 | static int ptrace_bts_config(struct task_struct *child, | 696 | static int ptrace_bts_config(struct task_struct *child, |
697 | long cfg_size, | ||
693 | const struct ptrace_bts_config __user *ucfg) | 698 | const struct ptrace_bts_config __user *ucfg) |
694 | { | 699 | { |
695 | struct ptrace_bts_config cfg; | 700 | struct ptrace_bts_config cfg; |
696 | int bts_size, ret = 0; | 701 | int bts_size, ret = 0; |
697 | void *ds; | 702 | void *ds; |
698 | 703 | ||
704 | if (cfg_size < sizeof(cfg)) | ||
705 | return -EIO; | ||
706 | |||
699 | if (copy_from_user(&cfg, ucfg, sizeof(cfg))) | 707 | if (copy_from_user(&cfg, ucfg, sizeof(cfg))) |
700 | return -EFAULT; | 708 | return -EFAULT; |
701 | 709 | ||
710 | if ((int)cfg.size < 0) | ||
711 | return -EINVAL; | ||
712 | |||
702 | bts_size = 0; | 713 | bts_size = 0; |
703 | ds = (void *)child->thread.ds_area_msr; | 714 | ds = (void *)child->thread.ds_area_msr; |
704 | if (ds) { | 715 | if (ds) { |
@@ -734,6 +745,8 @@ static int ptrace_bts_config(struct task_struct *child, | |||
734 | else | 745 | else |
735 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | 746 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); |
736 | 747 | ||
748 | ret = sizeof(cfg); | ||
749 | |||
737 | out: | 750 | out: |
738 | if (child->thread.debugctlmsr) | 751 | if (child->thread.debugctlmsr) |
739 | set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | 752 | set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); |
@@ -749,11 +762,15 @@ errout: | |||
749 | } | 762 | } |
750 | 763 | ||
751 | static int ptrace_bts_status(struct task_struct *child, | 764 | static int ptrace_bts_status(struct task_struct *child, |
765 | long cfg_size, | ||
752 | struct ptrace_bts_config __user *ucfg) | 766 | struct ptrace_bts_config __user *ucfg) |
753 | { | 767 | { |
754 | void *ds = (void *)child->thread.ds_area_msr; | 768 | void *ds = (void *)child->thread.ds_area_msr; |
755 | struct ptrace_bts_config cfg; | 769 | struct ptrace_bts_config cfg; |
756 | 770 | ||
771 | if (cfg_size < sizeof(cfg)) | ||
772 | return -EIO; | ||
773 | |||
757 | memset(&cfg, 0, sizeof(cfg)); | 774 | memset(&cfg, 0, sizeof(cfg)); |
758 | 775 | ||
759 | if (ds) { | 776 | if (ds) { |
@@ -923,12 +940,12 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
923 | 940 | ||
924 | case PTRACE_BTS_CONFIG: | 941 | case PTRACE_BTS_CONFIG: |
925 | ret = ptrace_bts_config | 942 | ret = ptrace_bts_config |
926 | (child, (struct ptrace_bts_config __user *)addr); | 943 | (child, data, (struct ptrace_bts_config __user *)addr); |
927 | break; | 944 | break; |
928 | 945 | ||
929 | case PTRACE_BTS_STATUS: | 946 | case PTRACE_BTS_STATUS: |
930 | ret = ptrace_bts_status | 947 | ret = ptrace_bts_status |
931 | (child, (struct ptrace_bts_config __user *)addr); | 948 | (child, data, (struct ptrace_bts_config __user *)addr); |
932 | break; | 949 | break; |
933 | 950 | ||
934 | case PTRACE_BTS_SIZE: | 951 | case PTRACE_BTS_SIZE: |
@@ -946,7 +963,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
946 | 963 | ||
947 | case PTRACE_BTS_DRAIN: | 964 | case PTRACE_BTS_DRAIN: |
948 | ret = ptrace_bts_drain | 965 | ret = ptrace_bts_drain |
949 | (child, (struct bts_struct __user *) addr); | 966 | (child, data, (struct bts_struct __user *) addr); |
950 | break; | 967 | break; |
951 | 968 | ||
952 | default: | 969 | default: |
diff --git a/include/asm-x86/ptrace-abi.h b/include/asm-x86/ptrace-abi.h index 32fe137822bf..bcf67044754c 100644 --- a/include/asm-x86/ptrace-abi.h +++ b/include/asm-x86/ptrace-abi.h | |||
@@ -99,13 +99,15 @@ struct ptrace_bts_config { | |||
99 | 99 | ||
100 | #define PTRACE_BTS_CONFIG 40 | 100 | #define PTRACE_BTS_CONFIG 40 |
101 | /* Configure branch trace recording. | 101 | /* Configure branch trace recording. |
102 | DATA is ignored, ADDR points to a struct ptrace_bts_config. | 102 | ADDR points to a struct ptrace_bts_config. |
103 | DATA gives the size of that buffer. | ||
103 | A new buffer is allocated, iff the size changes. | 104 | A new buffer is allocated, iff the size changes. |
105 | Returns the number of bytes read. | ||
104 | */ | 106 | */ |
105 | #define PTRACE_BTS_STATUS 41 | 107 | #define PTRACE_BTS_STATUS 41 |
106 | /* Return the current configuration. | 108 | /* Return the current configuration in a struct ptrace_bts_config |
107 | DATA is ignored, ADDR points to a struct ptrace_bts_config | 109 | pointed to by ADDR; DATA gives the size of that buffer. |
108 | that will contain the result. | 110 | Returns the number of bytes written. |
109 | */ | 111 | */ |
110 | #define PTRACE_BTS_SIZE 42 | 112 | #define PTRACE_BTS_SIZE 42 |
111 | /* Return the number of available BTS records. | 113 | /* Return the number of available BTS records. |
@@ -123,8 +125,8 @@ struct ptrace_bts_config { | |||
123 | */ | 125 | */ |
124 | #define PTRACE_BTS_DRAIN 45 | 126 | #define PTRACE_BTS_DRAIN 45 |
125 | /* Read all available BTS records and clear the buffer. | 127 | /* Read all available BTS records and clear the buffer. |
126 | DATA is ignored. ADDR points to an array of struct bts_struct of | 128 | ADDR points to an array of struct bts_struct. |
127 | suitable size. | 129 | DATA gives the size of that buffer. |
128 | BTS records are read from oldest to newest. | 130 | BTS records are read from oldest to newest. |
129 | Returns number of BTS records drained. | 131 | Returns number of BTS records drained. |
130 | */ | 132 | */ |