Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp153708pxj; Mon, 17 May 2021 23:43:21 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzbWqvVTV/uey0cfoJQdabRvqAMTwAXxGJyZTAo4DrcM6viUP1a+ET2pCWHI/QycfFgPctI X-Received: by 2002:a05:6402:7d4:: with SMTP id u20mr5193326edy.302.1621320201591; Mon, 17 May 2021 23:43:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1621320201; cv=none; d=google.com; s=arc-20160816; b=dqkSO8H47R0TfLboHDh1SufCarfbMaDggi1qzAgnoy55uZSubCVz5obtsuGh5fr5kb sy1OaRtEw96BV625554xrPRB3NE8RENAspQDYP5HTodOplWjd0QYmzt7Cv8WmHwbqBOX Oeeo/oGPnS0r1D7NcLLRsgBvrmcLimx1iGTk95eMH7f6BA5wgiMP/rLst5DHhkrWeWM0 vaR2j0EYnAQUpFPcXVGeufbQ8t964mw8jF1C3ldEcoVxg3UFToCJHHCZmLrmJ1CWQdFS BvEik5LQELQbrXy4z0XvBvHIHijZWXs3NG5d2GEW9R89koCk9hRik7buAA5G5Z1nsyrb +7rQ== 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=duOOQPKw853YpwfcDDVwM5wwDLgaH5fScKGuEuXm1y0=; b=FO4KMduV/9poz6tR8MA5pg0TczJ5m8Ktgj0nYdDE7psQFamcLWb3aiM58d4B6Pkwu2 bMflFzI1az5RvmIdfRGNRt8zDt8KAZ3SJQ+IxXYd0CAeFR2H4OnlZiC/AiR9qNjwFY/7 O2gxVG44I1ioX6hgTXNNAtE6Z7oC1dB+zzrUtmvpwjoY8Ge7D52Jr49cm+iGVbpjL3CK VWyGYWYTt8VFPD752KhvbaanjJz2LCnCKGBFbYjpI0YxURmyaGSYeSDyCkArLMq19ZYa llVUJWzYiY9+o4xKbf8UzZGsXc5augigJQg3/jH6zMLw7RqJuhTu+GwkcILH9577BpFT dVjw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b="YXE/+3vx"; 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=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id i5si16502402eje.413.2021.05.17.23.42.58; Mon, 17 May 2021 23:43:21 -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=@linuxfoundation.org header.s=korg header.b="YXE/+3vx"; 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=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240950AbhEQOwp (ORCPT + 99 others); Mon, 17 May 2021 10:52:45 -0400 Received: from mail.kernel.org ([198.145.29.99]:50784 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241054AbhEQOmM (ORCPT ); Mon, 17 May 2021 10:42:12 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id D212D6194F; Mon, 17 May 2021 14:19:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1621261170; bh=xQTCBefknPJwn8xUmwIKtxb/TZeDjtrLq7fIdlDwqbU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YXE/+3vxpM90Vsr9drdDbKrlbwByyQdiNogHLbn9hftVauLLAIu9v7tBUvy8xxmV8 pNkdBmqYRbKC1IU2YnoTKhaETFECQhfHCWydF58yC18UcOTxlkaxv4RcTYUZ8dJ3zT gM1WZyt/NGViUP+yqODKaRy/UrhUQHuZnmTa2FfE= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Heikki Krogerus , Jack Pham , Subbaraman Narayanamurthy Subject: [PATCH 5.12 317/363] usb: typec: ucsi: Retrieve all the PDOs instead of just the first 4 Date: Mon, 17 May 2021 16:03:03 +0200 Message-Id: <20210517140313.321929842@linuxfoundation.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210517140302.508966430@linuxfoundation.org> References: <20210517140302.508966430@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: Jack Pham commit 1f4642b72be79757f050924a9b9673b6a02034bc upstream. commit 4dbc6a4ef06d ("usb: typec: ucsi: save power data objects in PD mode") introduced retrieval of the PDOs when connected to a PD-capable source. But only the first 4 PDOs are received since that is the maximum number that can be fetched at a time given the MESSAGE_IN length limitation (16 bytes). However, as per the PD spec a connected source may advertise up to a maximum of 7 PDOs. If such a source is connected it's possible the PPM could have negotiated a power contract with one of the PDOs at index greater than 4, and would be reflected in the request data object's (RDO) object position field. This would result in an out-of-bounds access when the rdo_index() is used to index into the src_pdos array in ucsi_psy_get_voltage_now(). With the help of the UBSAN -fsanitize=array-bounds checker enabled this exact issue is revealed when connecting to a PD source adapter that advertise 5 PDOs and the PPM enters a contract having selected the 5th one. [ 151.545106][ T70] Unexpected kernel BRK exception at EL1 [ 151.545112][ T70] Internal error: BRK handler: f2005512 [#1] PREEMPT SMP ... [ 151.545499][ T70] pc : ucsi_psy_get_prop+0x208/0x20c [ 151.545507][ T70] lr : power_supply_show_property+0xc0/0x328 ... [ 151.545542][ T70] Call trace: [ 151.545544][ T70] ucsi_psy_get_prop+0x208/0x20c [ 151.545546][ T70] power_supply_uevent+0x1a4/0x2f0 [ 151.545550][ T70] dev_uevent+0x200/0x384 [ 151.545555][ T70] kobject_uevent_env+0x1d4/0x7e8 [ 151.545557][ T70] power_supply_changed_work+0x174/0x31c [ 151.545562][ T70] process_one_work+0x244/0x6f0 [ 151.545564][ T70] worker_thread+0x3e0/0xa64 We can resolve this by instead retrieving and storing up to the maximum of 7 PDOs in the con->src_pdos array. This would involve two calls to the GET_PDOS command. Fixes: 992a60ed0d5e ("usb: typec: ucsi: register with power_supply class") Fixes: 4dbc6a4ef06d ("usb: typec: ucsi: save power data objects in PD mode") Cc: stable@vger.kernel.org Reported-and-tested-by: Subbaraman Narayanamurthy Reviewed-by: Heikki Krogerus Signed-off-by: Jack Pham Link: https://lore.kernel.org/r/20210503074611.30973-1-jackp@codeaurora.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 41 ++++++++++++++++++++++++++++++++--------- drivers/usb/typec/ucsi/ucsi.h | 6 ++++-- 2 files changed, 36 insertions(+), 11 deletions(-) --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -495,7 +495,8 @@ static void ucsi_unregister_altmodes(str } } -static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner) +static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner, + u32 *pdos, int offset, int num_pdos) { struct ucsi *ucsi = con->ucsi; u64 command; @@ -503,17 +504,39 @@ static void ucsi_get_pdos(struct ucsi_co command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner); - command |= UCSI_GET_PDOS_NUM_PDOS(UCSI_MAX_PDOS - 1); + command |= UCSI_GET_PDOS_PDO_OFFSET(offset); + command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1); command |= UCSI_GET_PDOS_SRC_PDOS; - ret = ucsi_send_command(ucsi, command, con->src_pdos, - sizeof(con->src_pdos)); - if (ret < 0) { + ret = ucsi_send_command(ucsi, command, pdos + offset, + num_pdos * sizeof(u32)); + if (ret < 0) dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret); + if (ret == 0 && offset == 0) + dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n"); + + return ret; +} + +static void ucsi_get_src_pdos(struct ucsi_connector *con, int is_partner) +{ + int ret; + + /* UCSI max payload means only getting at most 4 PDOs at a time */ + ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS); + if (ret < 0) return; - } + con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */ - if (ret == 0) - dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n"); + if (con->num_pdos < UCSI_MAX_PDOS) + return; + + /* get the remaining PDOs, if any */ + ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS, + PDO_MAX_OBJECTS - UCSI_MAX_PDOS); + if (ret < 0) + return; + + con->num_pdos += ret / sizeof(u32); } static void ucsi_pwr_opmode_change(struct ucsi_connector *con) @@ -522,7 +545,7 @@ static void ucsi_pwr_opmode_change(struc case UCSI_CONSTAT_PWR_OPMODE_PD: con->rdo = con->status.request_data_obj; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); - ucsi_get_pdos(con, 1); + ucsi_get_src_pdos(con, 1); break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: con->rdo = 0; --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -8,6 +8,7 @@ #include #include #include +#include #include /* -------------------------------------------------------------------------- */ @@ -134,7 +135,9 @@ void ucsi_connector_change(struct ucsi * /* GET_PDOS command bits */ #define UCSI_GET_PDOS_PARTNER_PDO(_r_) ((u64)(_r_) << 23) +#define UCSI_GET_PDOS_PDO_OFFSET(_r_) ((u64)(_r_) << 24) #define UCSI_GET_PDOS_NUM_PDOS(_r_) ((u64)(_r_) << 32) +#define UCSI_MAX_PDOS (4) #define UCSI_GET_PDOS_SRC_PDOS ((u64)1 << 34) /* -------------------------------------------------------------------------- */ @@ -302,7 +305,6 @@ struct ucsi { #define UCSI_MAX_SVID 5 #define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) -#define UCSI_MAX_PDOS (4) #define UCSI_TYPEC_VSAFE5V 5000 #define UCSI_TYPEC_1_5_CURRENT 1500 @@ -330,7 +332,7 @@ struct ucsi_connector { struct power_supply *psy; struct power_supply_desc psy_desc; u32 rdo; - u32 src_pdos[UCSI_MAX_PDOS]; + u32 src_pdos[PDO_MAX_OBJECTS]; int num_pdos; struct usb_role_switch *usb_role_sw;