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 /arch/x86/kernel/ptrace.c | |
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>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 25 |
1 files changed, 21 insertions, 4 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: |