Commit da35c371 authored by Markus Metzger's avatar Markus Metzger Committed by Ingo Molnar

x86, ptrace: rlimit BTS buffer allocation

Check the rlimit of the tracing task for total and locked memory when allocating the BTS buffer.
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 59e87cdc
...@@ -620,12 +620,80 @@ static int ptrace_bts_drain(struct task_struct *child, ...@@ -620,12 +620,80 @@ static int ptrace_bts_drain(struct task_struct *child,
return i; 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, static int ptrace_bts_config(struct task_struct *child,
const struct ptrace_bts_config __user *ucfg) const struct ptrace_bts_config __user *ucfg)
{ {
struct ptrace_bts_config cfg; struct ptrace_bts_config cfg;
unsigned long debugctl_mask; int bts_size, ret = 0;
int bts_size, ret;
void *ds; void *ds;
if (copy_from_user(&cfg, ucfg, sizeof(cfg))) if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
...@@ -638,59 +706,46 @@ static int ptrace_bts_config(struct task_struct *child, ...@@ -638,59 +706,46 @@ static int ptrace_bts_config(struct task_struct *child,
if (bts_size < 0) if (bts_size < 0)
return bts_size; return bts_size;
} }
cfg.size = PAGE_ALIGN(cfg.size);
if (bts_size != 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) 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; 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)
if (cfg.flags & PTRACE_BTS_O_SIGNAL) { ret = ds_set_overflow(ds, DS_O_SIGNAL);
ret = ds_set_overflow(ds, DS_O_SIGNAL); else
} else { ret = ds_set_overflow(ds, DS_O_WRAP);
ret = ds_set_overflow(ds, DS_O_WRAP); if (ret < 0)
} goto errout;
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 (!child->thread.debugctlmsr) if (cfg.flags & PTRACE_BTS_O_TRACE)
clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); 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); set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
else else
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); 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, static int ptrace_bts_status(struct task_struct *child,
...@@ -726,7 +781,7 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk, ...@@ -726,7 +781,7 @@ void ptrace_bts_take_timestamp(struct task_struct *tsk,
{ {
struct bts_struct rec = { struct bts_struct rec = {
.qualifier = qualifier, .qualifier = qualifier,
.variant.jiffies = jiffies .variant.jiffies = jiffies_64
}; };
ptrace_bts_write_record(tsk, &rec); ptrace_bts_write_record(tsk, &rec);
...@@ -743,10 +798,12 @@ void ptrace_disable(struct task_struct *child) ...@@ -743,10 +798,12 @@ void ptrace_disable(struct task_struct *child)
#ifdef TIF_SYSCALL_EMU #ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif #endif
ptrace_bts_config(child, /* options = */ 0);
if (child->thread.ds_area_msr) { if (child->thread.ds_area_msr) {
ds_free((void **)&child->thread.ds_area_msr); ptrace_bts_realloc(child, 0, 0);
clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); 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);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment