summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/ptrace.c149
1 files changed, 103 insertions, 46 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 3e34b14e8846..88ed1e74cee9 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -620,12 +620,80 @@ static int ptrace_bts_drain(struct task_struct *child,
return i;
}
+static int ptrace_bts_realloc(struct task_struct *child,
+ int size, int reduce_size)
+{
+ unsigned long rlim, vm;
+ int ret, old_size;
+
+ if (size < 0)
+ return -EINVAL;
+
+ old_size = ds_get_bts_size((void *)child->thread.ds_area_msr);
+ if (old_size < 0)
+ return old_size;
+
+ ret = ds_free((void **)&child->thread.ds_area_msr);
+ if (ret < 0)
+ goto out;
+
+ size >>= PAGE_SHIFT;
+ old_size >>= PAGE_SHIFT;
+
+ current->mm->total_vm -= old_size;
+ current->mm->locked_vm -= old_size;
+
+ if (size == 0)
+ goto out;
+
+ rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+ vm = current->mm->total_vm + size;
+ if (rlim < vm) {
+ ret = -ENOMEM;
+
+ if (!reduce_size)
+ goto out;
+
+ size = rlim - current->mm->total_vm;
+ if (size <= 0)
+ goto out;
+ }
+
+ rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT;
+ vm = current->mm->locked_vm + size;
+ if (rlim < vm) {
+ ret = -ENOMEM;
+
+ if (!reduce_size)
+ goto out;
+
+ size = rlim - current->mm->locked_vm;
+ if (size <= 0)
+ goto out;
+ }
+
+ ret = ds_allocate((void **)&child->thread.ds_area_msr,
+ size << PAGE_SHIFT);
+ if (ret < 0)
+ goto out;
+
+ current->mm->total_vm += size;
+ current->mm->locked_vm += size;
+
+out:
+ if (child->thread.ds_area_msr)
+ set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+ else
+ clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+
+ return ret;
+}
+
static int ptrace_bts_config(struct task_struct *child,
const struct ptrace_bts_config __user *ucfg)
{
struct ptrace_bts_config cfg;
- unsigned long debugctl_mask;
- int bts_size, ret;
+ int bts_size, ret = 0;
void *ds;
if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
@@ -638,59 +706,46 @@ static int ptrace_bts_config(struct task_struct *child,
if (bts_size < 0)
return bts_size;
}
+ cfg.size = PAGE_ALIGN(cfg.size);
if (bts_size != cfg.size) {
- ret = ds_free((void **)&child->thread.ds_area_msr);
+ ret = ptrace_bts_realloc(child, cfg.size,
+ cfg.flags & PTRACE_BTS_O_CUT_SIZE);
if (ret < 0)
- return ret;
+ goto errout;
- if (cfg.size > 0)
- ret = ds_allocate((void **)&child->thread.ds_area_msr,
- cfg.size);
ds = (void *)child->thread.ds_area_msr;
- if (ds)
- set_tsk_thread_flag(child, TIF_DS_AREA_MSR);
- else
- clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
-
- if (ret < 0)
- return ret;
-
- bts_size = ds_get_bts_size(ds);
- if (bts_size <= 0)
- return bts_size;
}
- if (ds) {
- if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
- ret = ds_set_overflow(ds, DS_O_SIGNAL);
- } else {
- ret = ds_set_overflow(ds, DS_O_WRAP);
- }
- if (ret < 0)
- return ret;
- }
-
- debugctl_mask = ds_debugctl_mask();
- if (ds && (cfg.flags & PTRACE_BTS_O_TRACE)) {
- child->thread.debugctlmsr |= debugctl_mask;
- set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
- } else {
- /* there is no way for us to check whether we 'own'
- * the respective bits in the DEBUGCTL MSR, we're
- * about to clear */
- child->thread.debugctlmsr &= ~debugctl_mask;
+ if (cfg.flags & PTRACE_BTS_O_SIGNAL)
+ ret = ds_set_overflow(ds, DS_O_SIGNAL);
+ else
+ ret = ds_set_overflow(ds, DS_O_WRAP);
+ if (ret < 0)
+ goto errout;
- if (!child->thread.debugctlmsr)
- clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
- }
+ if (cfg.flags & PTRACE_BTS_O_TRACE)
+ child->thread.debugctlmsr |= ds_debugctl_mask();
+ else
+ child->thread.debugctlmsr &= ~ds_debugctl_mask();
- if (ds && (cfg.flags & PTRACE_BTS_O_SCHED))
+ if (cfg.flags & PTRACE_BTS_O_SCHED)
set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
else
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
- return 0;
+out:
+ if (child->thread.debugctlmsr)
+ set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+ else
+ clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+
+ return ret;
+
+errout:
+ child->thread.debugctlmsr &= ~ds_debugctl_mask();
+ clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
+ goto out;
}
static int ptrace_bts_status(struct task_struct *child,
@@ -726,7 +781,7 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk,
{
struct bts_struct rec = {
.qualifier = qualifier,
- .variant.jiffies = jiffies
+ .variant.jiffies = jiffies_64
};
ptrace_bts_write_record(tsk, &rec);
@@ -743,10 +798,12 @@ void ptrace_disable(struct task_struct *child)
#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif
- ptrace_bts_config(child, /* options = */ 0);
if (child->thread.ds_area_msr) {
- ds_free((void **)&child->thread.ds_area_msr);
- clear_tsk_thread_flag(child, TIF_DS_AREA_MSR);
+ ptrace_bts_realloc(child, 0, 0);
+ child->thread.debugctlmsr &= ~ds_debugctl_mask();
+ if (!child->thread.debugctlmsr)
+ clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+ clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
}
}