Received: by 2002:a05:6a10:16a7:0:0:0:0 with SMTP id gp39csp3210951pxb; Mon, 9 Nov 2020 05:41:52 -0800 (PST) X-Google-Smtp-Source: ABdhPJzoZYd8L5acLjwgDapT5p1MZ9fP/oyylkLkdZNtQ1JwEE+0U0VI0lumE8L9wfjYRMzP3rKv X-Received: by 2002:a17:906:52c6:: with SMTP id w6mr14318367ejn.199.1604929312064; Mon, 09 Nov 2020 05:41:52 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1604929312; cv=none; d=google.com; s=arc-20160816; b=tpYPTVyaFjB7jrcNI7z2Y7fRnOBdzTjXfSBgd9tH5xQev5fH4OzRa/1IRvZv5YQnRE dme4ERK489XySlLFFAoF/f2w4NNJCLgenP1Q3dNI2tIfVDOCMWayEpdvknwd9PLGw8xK pr2CUqEAgrZ65qhqMx+t0SynBsSOAhs7R7wvt6PJmjbnp9Q3CwiS53tanLCFNggLjw5t cmvzdxlmYtS0Wh9ckXsC32fKWMM5HXm0BTSQKM7uPo9odBnj6sqwPTdfN2eOjwXAnbm+ UhMYBwI3bzogY6rlrXoKRgGYE8ka1oD4Bx1sOWK0NehfJqv1D4Goj+r0HYZfi3s73PLS O2Ug== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=uYmSlAVLRm6pSaJxLxqx0yez913HvfcFaCvuk2Re/lE=; b=Wm64YsrDUi9VeT+pL6WRMORKwDLsMKTcya9d4/dZBf8CUIgI3qBz/08DeLl5nQYohh 9BkBy3UUxdBvx0L3SKn2q4Ut3u3e22qLQ8Sb3YKSjBprg4CV02fQqJ9w6rEAg34Cy3H2 Z77MJbOCredPKUPXXhFnWf8HqY96EKgxwUV6jAkmPKxZRQ5aO/2qpnWBEyEr94pZKqo8 +Bp+upNlfG2l55L0SEr3+/r5KbAiknvv+ep3iXYCVzW+4pBFb6i700DKa3ZhALSV9b22 vgpv4SxHyLByYs9iuwc+OMfeaxuBr1ZNvUAJZVIg4kH76IfW4knq8q3/v1KSz1Z7CfwG aTCg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=RP5i+g1n; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id g20si1357178eje.577.2020.11.09.05.41.26; Mon, 09 Nov 2020 05:41:52 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=RP5i+g1n; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388047AbgKINkN (ORCPT + 99 others); Mon, 9 Nov 2020 08:40:13 -0500 Received: from mail.kernel.org ([198.145.29.99]:56030 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730621AbgKINCH (ORCPT ); Mon, 9 Nov 2020 08:02:07 -0500 Received: from localhost (83-86-74-64.cable.dynamic.v4.ziggo.nl [83.86.74.64]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id BDDA420663; Mon, 9 Nov 2020 13:02:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604926926; bh=X6Nr+6YyM5f09N4tuATjBtDmmY6OknMtVdrFFO/CdqQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RP5i+g1nzupQojBH8mXdDTflhsf7NwATThkpZpnDc1ZBjJW93FdO8xjrI80tere+9 CVa1yQYuRVoF8LMKwe7gi/G3URPqsKu+lu2Rvwd6VEZE3cpXIIaU+X/OsFhGUstrv+ Xbb8p1StTBK+srsjnWZOFclDSsKeSF2lWpykzkuc= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Douglas Anderson , Matthias Kaehlcke , Will Deacon , Russell King , Sasha Levin Subject: [PATCH 4.9 021/117] ARM: 8997/2: hw_breakpoint: Handle inexact watchpoint addresses Date: Mon, 9 Nov 2020 13:54:07 +0100 Message-Id: <20201109125026.657579957@linuxfoundation.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201109125025.630721781@linuxfoundation.org> References: <20201109125025.630721781@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Douglas Anderson [ Upstream commit 22c9e58299e5f18274788ce54c03d4fb761e3c5d ] This is commit fdfeff0f9e3d ("arm64: hw_breakpoint: Handle inexact watchpoint addresses") but ported to arm32, which has the same problem. This problem was found by Android CTS tests, notably the "watchpoint_imprecise" test [1]. I tested locally against a copycat (simplified) version of the test though. [1] https://android.googlesource.com/platform/bionic/+/master/tests/sys_ptrace_test.cpp Link: https://lkml.kernel.org/r/20191019111216.1.I82eae759ca6dc28a245b043f485ca490e3015321@changeid Signed-off-by: Douglas Anderson Reviewed-by: Matthias Kaehlcke Acked-by: Will Deacon Signed-off-by: Russell King Signed-off-by: Sasha Levin --- arch/arm/kernel/hw_breakpoint.c | 100 +++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 28 deletions(-) diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 283084f6286d9..671dbc28e5d46 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -688,6 +688,40 @@ static void disable_single_step(struct perf_event *bp) arch_install_hw_breakpoint(bp); } +/* + * Arm32 hardware does not always report a watchpoint hit address that matches + * one of the watchpoints set. It can also report an address "near" the + * watchpoint if a single instruction access both watched and unwatched + * addresses. There is no straight-forward way, short of disassembling the + * offending instruction, to map that address back to the watchpoint. This + * function computes the distance of the memory access from the watchpoint as a + * heuristic for the likelyhood that a given access triggered the watchpoint. + * + * See this same function in the arm64 platform code, which has the same + * problem. + * + * The function returns the distance of the address from the bytes watched by + * the watchpoint. In case of an exact match, it returns 0. + */ +static u32 get_distance_from_watchpoint(unsigned long addr, u32 val, + struct arch_hw_breakpoint_ctrl *ctrl) +{ + u32 wp_low, wp_high; + u32 lens, lene; + + lens = __ffs(ctrl->len); + lene = __fls(ctrl->len); + + wp_low = val + lens; + wp_high = val + lene; + if (addr < wp_low) + return wp_low - addr; + else if (addr > wp_high) + return addr - wp_high; + else + return 0; +} + static int watchpoint_fault_on_uaccess(struct pt_regs *regs, struct arch_hw_breakpoint *info) { @@ -697,23 +731,25 @@ static int watchpoint_fault_on_uaccess(struct pt_regs *regs, static void watchpoint_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - int i, access; - u32 val, ctrl_reg, alignment_mask; + int i, access, closest_match = 0; + u32 min_dist = -1, dist; + u32 val, ctrl_reg; struct perf_event *wp, **slots; struct arch_hw_breakpoint *info; struct arch_hw_breakpoint_ctrl ctrl; slots = this_cpu_ptr(wp_on_reg); + /* + * Find all watchpoints that match the reported address. If no exact + * match is found. Attribute the hit to the closest watchpoint. + */ + rcu_read_lock(); for (i = 0; i < core_num_wrps; ++i) { - rcu_read_lock(); - wp = slots[i]; - if (wp == NULL) - goto unlock; + continue; - info = counter_arch_bp(wp); /* * The DFAR is an unknown value on debug architectures prior * to 7.1. Since we only allow a single watchpoint on these @@ -722,33 +758,31 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr, */ if (debug_arch < ARM_DEBUG_ARCH_V7_1) { BUG_ON(i > 0); + info = counter_arch_bp(wp); info->trigger = wp->attr.bp_addr; } else { - if (info->ctrl.len == ARM_BREAKPOINT_LEN_8) - alignment_mask = 0x7; - else - alignment_mask = 0x3; - - /* Check if the watchpoint value matches. */ - val = read_wb_reg(ARM_BASE_WVR + i); - if (val != (addr & ~alignment_mask)) - goto unlock; - - /* Possible match, check the byte address select. */ - ctrl_reg = read_wb_reg(ARM_BASE_WCR + i); - decode_ctrl_reg(ctrl_reg, &ctrl); - if (!((1 << (addr & alignment_mask)) & ctrl.len)) - goto unlock; - /* Check that the access type matches. */ if (debug_exception_updates_fsr()) { access = (fsr & ARM_FSR_ACCESS_MASK) ? HW_BREAKPOINT_W : HW_BREAKPOINT_R; if (!(access & hw_breakpoint_type(wp))) - goto unlock; + continue; } + val = read_wb_reg(ARM_BASE_WVR + i); + ctrl_reg = read_wb_reg(ARM_BASE_WCR + i); + decode_ctrl_reg(ctrl_reg, &ctrl); + dist = get_distance_from_watchpoint(addr, val, &ctrl); + if (dist < min_dist) { + min_dist = dist; + closest_match = i; + } + /* Is this an exact match? */ + if (dist != 0) + continue; + /* We have a winner. */ + info = counter_arch_bp(wp); info->trigger = addr; } @@ -770,13 +804,23 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr, * we can single-step over the watchpoint trigger. */ if (!is_default_overflow_handler(wp)) - goto unlock; - + continue; step: enable_single_step(wp, instruction_pointer(regs)); -unlock: - rcu_read_unlock(); } + + if (min_dist > 0 && min_dist != -1) { + /* No exact match found. */ + wp = slots[closest_match]; + info = counter_arch_bp(wp); + info->trigger = addr; + pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); + perf_bp_event(wp, regs); + if (is_default_overflow_handler(wp)) + enable_single_step(wp, instruction_pointer(regs)); + } + + rcu_read_unlock(); } static void watchpoint_single_step_handler(unsigned long pc) -- 2.27.0