quadd_callchain_store(struct quadd_callchain *cc,
unsigned long ip)
{
- if (ip && cc->nr < QUADD_MAX_STACK_DEPTH) {
- if (cc->cs_64)
- cc->ip_64[cc->nr++] = ip;
- else
- cc->ip_32[cc->nr++] = ip;
+ if (!ip) {
+ cc->unw_rc = QUADD_URC_PC_INCORRECT;
+ return 0;
+ }
- return 1;
+ if (cc->nr >= QUADD_MAX_STACK_DEPTH) {
+ cc->unw_rc = QUADD_URC_LEVEL_TOO_DEEP;
+ return 0;
}
- return 0;
+
+ if (cc->cs_64)
+ cc->ip_64[cc->nr++] = ip;
+ else
+ cc->ip_32[cc->nr++] = ip;
+
+ return 1;
}
static unsigned long __user *
struct quadd_callchain *cc,
struct vm_area_struct *stack_vma)
{
+ int nr_added;
unsigned long value, value_lr = 0, value_fp = 0;
unsigned long __user *fp_prev = NULL;
if (!is_vma_addr((unsigned long)tail, stack_vma, sizeof(*tail)))
return NULL;
- if (__copy_from_user_inatomic(&value, tail, sizeof(unsigned long)))
+ if (__copy_from_user_inatomic(&value, tail, sizeof(unsigned long))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
if (is_vma_addr(value, stack_vma, sizeof(value))) {
/* gcc thumb/clang frame */
return NULL;
if (__copy_from_user_inatomic(&value_lr, tail + 1,
- sizeof(value_lr)))
+ sizeof(value_lr))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
} else {
/* gcc arm frame */
if (__copy_from_user_inatomic(&value_fp, tail - 1,
- sizeof(value_fp)))
+ sizeof(value_fp))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
if (!is_vma_addr(value_fp, stack_vma, sizeof(value_fp)))
return NULL;
fp_prev = (unsigned long __user *)value_fp;
- if (value_lr < QUADD_USER_SPACE_MIN_ADDR)
+ if (value_lr < QUADD_USER_SPACE_MIN_ADDR) {
+ cc->unw_rc = QUADD_URC_PC_INCORRECT;
return NULL;
+ }
- quadd_callchain_store(cc, value_lr);
+ nr_added = quadd_callchain_store(cc, value_lr);
+ if (nr_added == 0)
+ return NULL;
if (fp_prev <= tail)
return NULL;
struct mm_struct *mm = task->mm;
cc->nr = 0;
- cc->unw_method = QUADD_UNW_METHOD_FP;
+ cc->unw_rc = QUADD_URC_FP_INCORRECT;
- if (!regs || !mm)
+ if (!regs || !mm) {
+ cc->unw_rc = QUADD_URC_FAILURE;
return 0;
+ }
sp = quadd_user_stack_pointer(regs);
pc = instruction_pointer(regs);
return 0;
vma = find_vma(mm, sp);
- if (!vma)
+ if (!vma) {
+ cc->unw_rc = QUADD_URC_SP_INCORRECT;
return 0;
+ }
if (!is_vma_addr(fp, vma, sizeof(fp)))
return 0;
pr_warn_once("frame error: sp/fp: %#lx/%#lx, pc/lr: %#lx/%#lx, vma: %#lx-%#lx\n",
sp, fp, pc, quadd_user_link_register(regs),
vma->vm_start, vma->vm_end);
+ cc->unw_rc = QUADD_URC_EACCESS;
return 0;
}
if (__copy_from_user_inatomic(
&value,
(unsigned long __user *)fp + 1,
- sizeof(unsigned long)))
+ sizeof(unsigned long))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return 0;
+ }
vma_pc = find_vma(mm, pc);
read_lr = 1;
if (!read_lr || !is_vma_addr(value, vma_pc, sizeof(value))) {
/* gcc: fp --> short frame tail (fp) */
+ int nr_added;
unsigned long lr = quadd_user_link_register(regs);
- if (lr < QUADD_USER_SPACE_MIN_ADDR)
+ if (lr < QUADD_USER_SPACE_MIN_ADDR) {
+ cc->unw_rc = QUADD_URC_PC_INCORRECT;
return 0;
+ }
+
+ nr_added = quadd_callchain_store(cc, lr);
+ if (nr_added == 0)
+ return cc->nr;
- quadd_callchain_store(cc, lr);
tail = (unsigned long __user *)reg;
}
}
struct vm_area_struct *vma;
unsigned long __user *tail;
- if (!mm)
- goto out;
+ cc->unw_rc = QUADD_URC_FP_INCORRECT;
+
+ if (!mm) {
+ cc->unw_rc = QUADD_URC_FAILURE;
+ return cc->nr;
+ }
vma = find_vma(mm, cc->curr_sp);
- if (!vma)
- goto out;
+ if (!vma) {
+ cc->unw_rc = QUADD_URC_SP_INCORRECT;
+ return cc->nr;
+ }
tail = (unsigned long __user *)cc->curr_fp;
while (tail && !((unsigned long)tail & 0x3))
tail = user_backtrace(tail, cc, vma);
-out:
return cc->nr;
}
struct quadd_callchain *cc,
struct vm_area_struct *stack_vma)
{
+ int nr_added;
u32 value, value_lr = 0, value_fp = 0;
u32 __user *fp_prev = NULL;
if (!is_vma_addr((unsigned long)tail, stack_vma, sizeof(*tail)))
return NULL;
- if (__copy_from_user_inatomic(&value, tail, sizeof(value)))
+ if (__copy_from_user_inatomic(&value, tail, sizeof(value))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
if (is_vma_addr(value, stack_vma, sizeof(value))) {
/* gcc thumb/clang frame */
return NULL;
if (__copy_from_user_inatomic(&value_lr, tail + 1,
- sizeof(value_lr)))
+ sizeof(value_lr))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
} else {
/* gcc arm frame */
if (__copy_from_user_inatomic(&value_fp, tail - 1,
- sizeof(value_fp)))
+ sizeof(value_fp))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return NULL;
+ }
if (!is_vma_addr(value_fp, stack_vma, sizeof(value_fp)))
return NULL;
fp_prev = (u32 __user *)(unsigned long)value_fp;
- if (value_lr < QUADD_USER_SPACE_MIN_ADDR)
+ if (value_lr < QUADD_USER_SPACE_MIN_ADDR) {
+ cc->unw_rc = QUADD_URC_PC_INCORRECT;
return NULL;
+ }
- quadd_callchain_store(cc, value_lr);
+ nr_added = quadd_callchain_store(cc, value_lr);
+ if (nr_added == 0)
+ return NULL;
if (fp_prev <= tail)
return NULL;
struct mm_struct *mm = task->mm;
cc->nr = 0;
+ cc->unw_rc = QUADD_URC_FP_INCORRECT;
- if (!regs || !mm)
+ if (!regs || !mm) {
+ cc->unw_rc = QUADD_URC_FAILURE;
return 0;
+ }
sp = quadd_user_stack_pointer(regs);
pc = instruction_pointer(regs);
return 0;
vma = find_vma(mm, sp);
- if (!vma)
+ if (!vma) {
+ cc->unw_rc = QUADD_URC_SP_INCORRECT;
return 0;
+ }
if (!is_vma_addr(fp, vma, sizeof(fp)))
return 0;
pr_warn_once("frame error: sp/fp: %#x/%#x, pc/lr: %#x/%#x, vma: %#lx-%#lx\n",
sp, fp, pc, (u32)quadd_user_link_register(regs),
vma->vm_start, vma->vm_end);
+ cc->unw_rc = QUADD_URC_EACCESS;
return 0;
}
if (__copy_from_user_inatomic(
&value,
(u32 __user *)(fp + sizeof(u32)),
- sizeof(value)))
+ sizeof(value))) {
+ cc->unw_rc = QUADD_URC_EACCESS;
return 0;
+ }
vma_pc = find_vma(mm, pc);
read_lr = 1;
if (!read_lr || !is_vma_addr(value, vma_pc, sizeof(value))) {
/* gcc: fp --> short frame tail (fp) */
+ int nr_added;
u32 lr = quadd_user_link_register(regs);
- if (lr < QUADD_USER_SPACE_MIN_ADDR)
+ if (lr < QUADD_USER_SPACE_MIN_ADDR) {
+ cc->unw_rc = QUADD_URC_PC_INCORRECT;
return 0;
+ }
+
+ nr_added = quadd_callchain_store(cc, lr);
+ if (nr_added == 0)
+ return cc->nr;
- quadd_callchain_store(cc, lr);
tail = (u32 __user *)(unsigned long)reg;
}
}
struct vm_area_struct *vma;
u32 __user *tail;
- if (!mm)
- goto out;
+ cc->unw_rc = QUADD_URC_FP_INCORRECT;
+
+ if (!mm) {
+ cc->unw_rc = QUADD_URC_FAILURE;
+ return cc->nr;
+ }
vma = find_vma(mm, cc->curr_sp);
- if (!vma)
- goto out;
+ if (!vma) {
+ cc->unw_rc = QUADD_URC_SP_INCORRECT;
+ return cc->nr;
+ }
tail = (u32 __user *)cc->curr_fp;
while (tail && !((unsigned long)tail & 0x3))
tail = user_backtrace_compat(tail, cc, vma);
-out:
return cc->nr;
}
{
if (cc->nr > 0) {
int nr, nr_prev = cc->nr;
+
+ if (cc->unw_rc == QUADD_URC_LEVEL_TOO_DEEP)
+ return nr_prev;
+
#ifdef CONFIG_ARM64
if (compat_user_mode(regs))
nr = __user_backtrace_compat(cc, task);
struct quadd_parameters *param = &ctx->param;
cc->nr = 0;
+ cc->unw_method = QUADD_URC_FAILURE;
if (!regs)
return 0;