Received: by 2002:a05:6a10:9e8c:0:0:0:0 with SMTP id y12csp885306pxx; Tue, 27 Oct 2020 03:02:22 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwNGyy9U8HiK733jO2U67eiY0KAGtqhZ40Bu9rNl3oIc2xW+xQ87YpnbMTT5xI9YeL7cap6 X-Received: by 2002:a05:6402:1042:: with SMTP id e2mr1401594edu.320.1603792942200; Tue, 27 Oct 2020 03:02:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1603792942; cv=none; d=google.com; s=arc-20160816; b=l/oA/PFRjVvMZXTvsyiQIwX+tIBBYkqO3FEcxLCH8YvrvAtR9iXJg0wnK+0dsX7M6e 49w9QVRkZSnu3C2QQUkD2QSsYBan9rEI1M9QYYMxX1R54wV0ZJ87fWHecsHY4cmg5vAN 31CLE2gP7aB51pvwzPJRW77Fe+jTuWmPVtehVhMTdbEWdS4kSPZIOuOGvJBH973BIpm5 dNS4IS6BWAqNs4vZyuTZpi1iM4BGib360W5wLVYBhAUOwkTLaIstym7qyPdofnNt4LS6 EGa8EHGUUHdDY7/ry+DKLwwVzB611jpDhtrdNaMIFsdvw/UzmrsjIkQiTwVIaMzHIlwo kC5A== 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 :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=r9n2lbB72pLaz45SCYETY+DZ4mcKP5n83K0u2MQhIQE=; b=NjYEqkvcItzw5+8L5Z5mr35HKJ8yu+jI4gqbrSENkrquPV/lyHep182DPyQJ9FcbDy 7KangQDIm4U5H1jPftW7FHvli7pTO3/4eO0sC3LrCa+NXgS/gTQm5Z7YlngQ+Aa03smi +nzHwUskXAYNLQOMWBAtxQISFU5xtkobked6BQPPoo0zqlsUF0bYehDkmbzQHStB4tzc NCoONcvEeiJ5dqqZiC2pW+aIRuC2ceRMJXwRc/4VY92GyrtnHG32R03q2pK5Rre1Bgsz 8+8UIclK2heMFujxcjvRJerjt2AVu2al++i08p/7KXSvQjNKFS29REL1nkXWEslorMMo dYpw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=D2+yp9nK; 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=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id u20si471203edd.523.2020.10.27.03.01.58; Tue, 27 Oct 2020 03:02:22 -0700 (PDT) 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=D2+yp9nK; 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=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2436928AbgJZX6A (ORCPT + 99 others); Mon, 26 Oct 2020 19:58:00 -0400 Received: from mail.kernel.org ([198.145.29.99]:33688 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2410912AbgJZXzd (ORCPT ); Mon, 26 Oct 2020 19:55:33 -0400 Received: from sasha-vm.mshome.net (c-73-47-72-35.hsd1.nh.comcast.net [73.47.72.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 65CCE21D7B; Mon, 26 Oct 2020 23:55:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1603756532; bh=NyDl2x5vFVk0OJB3AGrrUBIoQ1PiPxh7GwOBa8WHi0I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=D2+yp9nK4VMiVuHnMsPdKqI+k03PSD09Yj42RMK2JW3JmNRkTh5prTU6fcqaqi+Nd mniCmr8Pu404bS+owbUaDZqrgh29Itnh+vUb3blqGfFY3EXv1k7NAaAPhIkvMEhIFc XXgooAt6G0NFDqGrbMhB5Ym6GiHmpJ69RUm1mu8g= From: Sasha Levin To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: Douglas Anderson , Matthias Kaehlcke , Will Deacon , Russell King , Sasha Levin , linux-arm-kernel@lists.infradead.org Subject: [PATCH AUTOSEL 5.4 12/80] ARM: 8997/2: hw_breakpoint: Handle inexact watchpoint addresses Date: Mon, 26 Oct 2020 19:54:08 -0400 Message-Id: <20201026235516.1025100-12-sashal@kernel.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201026235516.1025100-1-sashal@kernel.org> References: <20201026235516.1025100-1-sashal@kernel.org> MIME-Version: 1.0 X-stable: review X-Patchwork-Hint: Ignore 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 5f95e4b911a0b..7021ef0b4e71b 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -680,6 +680,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) { @@ -689,23 +723,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 @@ -714,33 +750,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; } @@ -762,13 +796,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.25.1