From nobody Fri Mar 04 19:50:40 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id EA43419F3420; Fri, 4 Mar 2022 19:50:40 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4K9JPD3V6Wz3BpG; Fri, 4 Mar 2022 19:50:40 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646423440; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=LUb8trWGxAbQYH5HJuZD18LU+0/VDqQzI2jXXBo05LA=; b=YEVc01Wd+kS/ngojF4nnUWx6qS95SpiPZF7PWi/xNKH/LRI0SE52+t1DVG02sYDRgwtAJF tK/UBfmzu0Wkao+G69OuUZTPj0FUTLPVk4tzVsk4MTReUD1d7TmhWEPf2+UkBx6DRk3Id5 +qqu4mobzPCad83QsOVJ3UkpJN1LGMh8AKAkIRXIsojOxbCbdfUiFlGib8xMEB6f1lrEo3 8wX7ccvk5nomhCK4cMxZwkANKm3GZkXtZunf5mOX9lDqc3AWMPlkA4aWae7yHOUid2s6KJ 49xVkcf0HbR1NWFndWRfsEP+pwP7vnsfS7T1BcSW9k4zZxRrPIWQA/l+61GzdQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 519F61289D; Fri, 4 Mar 2022 19:50:40 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 224Joero078097; Fri, 4 Mar 2022 19:50:40 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 224JoeLL078096; Fri, 4 Mar 2022 19:50:40 GMT (envelope-from git) Date: Fri, 4 Mar 2022 19:50:40 GMT Message-Id: <202203041950.224JoeLL078096@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: =?utf-8?Q?Stefan E=C3=9Fer?= Subject: git: f0163795c589 - stable/13 - dev/pci: fix potential panic due to bogus VPD data List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: se X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: f0163795c58982b8bd8d1034dd70f04c2fda5474 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646423440; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=LUb8trWGxAbQYH5HJuZD18LU+0/VDqQzI2jXXBo05LA=; b=YhE6uGNAIANpta57QMR3XVaAV1a2a5nNLqrPa1ikXrvyccE40cot6Byf0ZcRgjeMRXXjCb OBN8vX+EW04K7hZkYmYrKawZEiPg3NXXCpSseo13GYLVweWj0QasbDusbJg++wI8yxE6KH ZQ+0Y8v3CrrkmXAGx+JWY0D12wIj0e5Ft43lqjRgZo/vR2UWTk4jkbDOFmsZCX9Q5XHplS O9QBXiPXum4D6KbbrYUdaElUBaL6SvEIdg/+/Cg+ejk7ggHIxGcNnlAoa1K//vlmY9goq+ nlJUUs5zJ40CcYZOmgRifDlblw72ezfKuIJfKACEDBFQXD5XZku35URZ+4/daQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1646423440; a=rsa-sha256; cv=none; b=uvBAaMFCIERyVYbnPBU6FrSELC2seppwMVvzT/tTPA04EkWgIFBdX6x+HJaPaj556SN57v Q0I8X47w8So+otV/XuYhybVJCz14gyabNthoGG0RgK3652PCABVW3g1MBi4alsGlzdY6jH xK5IYq5MC/sL2pHy8jAJZAepiKKUjyiXtJBWYb+9Z24gFwsRxZwx7IsgyZ2DkcWh58s7vw 5BC2qluhJQDjB0+VqKaN70bZ3XRIi3Kkbz3l823wyB7I3i3rIgqUh9dVeVvo8BBWwSmzSd ahbf+ydvCGFkHpnwTtSmOZqYZnZ5ZyFYAxsmwZhGVNtISwno/ih3fRc8dIQYeQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by se: URL: https://cgit.FreeBSD.org/src/commit/?id=f0163795c58982b8bd8d1034dd70f04c2fda5474 commit f0163795c58982b8bd8d1034dd70f04c2fda5474 Author: Stefan Eßer AuthorDate: 2022-02-20 21:07:35 +0000 Commit: Stefan Eßer CommitDate: 2022-03-04 19:50:21 +0000 dev/pci: fix potential panic due to bogus VPD data A panic has been observed on a system with a Intel X520 dual LAN device. The panic is caused by a KASSERT() noticing that the amount of VPD data copied out to the pciconf command does not match the amount of data read from the device. The cause of the size mismatch was VPD data that started with 0x82, the VPD tag that indicates that a VPD ident follows, but with a length of more than 255 characters, which happens to be the maximum ident size supported by the API between kernel and the pciconf program. The data provided did not resemble an actual VPD identifier, and it can be assumed that the initial tag value 0x82 happens to be there by accident. An ident size of 255 far exceeds the sensible length of that data element, which is in the order of at most 30 to 40 bytes. This patch adds several consitstency checks to the VPD parser, the most critical being that ident lengths of more than 255 bytes are rejected. Other checks reject VPD with more than one ident tag or with an empty (zero length) ident string. This patch prevents the panic that occured when "pciconf -lV" was executed on the affected system. During the anaylsis of the issue and the VPD code it has been found that the VPD parser uses a state machine that accepts tags in any order and combination. This is a bad match for the actual VPD data, which has a very simple structure that can be parsed with a non-recursive direct descent parser (which always knows exactly which token to expect next). A review fpr a much simpler VPD parser that performs many more consistency checks and rejects invalid VPD has been proposed in review https://reviews.freebsd.org/D34268. (cherry picked from commit f01c863337f7b097d03069daee359b6b5ecd0279) --- sys/dev/pci/pci.c | 42 ++++++++++++++++++++++++++++++++++++------ sys/dev/pci/pci_user.c | 9 +++++---- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index b9d908bdd822..24f81b57ff11 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1094,6 +1094,7 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) int alloc, off; /* alloc/off for RO/W arrays */ int cksumvalid; int dflen; + int firstrecord; uint8_t byte; uint8_t byte2; @@ -1109,14 +1110,16 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) alloc = off = 0; /* shut up stupid gcc */ dflen = 0; /* shut up stupid gcc */ cksumvalid = -1; + firstrecord = 1; while (state >= 0) { if (vpd_nextbyte(&vrs, &byte)) { + pci_printf(cfg, "VPD read timed out\n"); state = -2; break; } #if 0 - printf("vpd: val: %#x, off: %d, bytesinval: %d, byte: %#hhx, " \ - "state: %d, remain: %d, name: %#x, i: %d\n", vrs.val, + pci_printf(cfg, "vpd: val: %#x, off: %d, bytesinval: %d, byte: " + "%#hhx, state: %d, remain: %d, name: %#x, i: %d\n", vrs.val, vrs.off, vrs.bytesinval, byte, state, remain, name, i); #endif switch (state) { @@ -1137,6 +1140,15 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) remain = byte & 0x7; name = (byte >> 3) & 0xf; } + if (firstrecord) { + if (name != 0x2) { + pci_printf(cfg, "VPD data does not " \ + "start with ident (%#x)\n", name); + state = -2; + break; + } + firstrecord = 0; + } if (vrs.off + remain - vrs.bytesinval > 0x8000) { pci_printf(cfg, "VPD data overflow, remain %#x\n", remain); @@ -1145,6 +1157,19 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) } switch (name) { case 0x2: /* String */ + if (cfg->vpd.vpd_ident != NULL) { + pci_printf(cfg, + "duplicate VPD ident record\n"); + state = -2; + break; + } + if (remain > 255) { + pci_printf(cfg, + "VPD ident length %d exceeds 255\n", + remain); + state = -2; + break; + } cfg->vpd.vpd_ident = malloc(remain + 1, M_DEVBUF, M_WAITOK); i = 0; @@ -1170,7 +1195,8 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) state = 5; break; default: /* Invalid data, abort */ - state = -1; + pci_printf(cfg, "invalid VPD name: %#x\n", name); + state = -2; break; } break; @@ -1208,8 +1234,7 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) * if this happens, we can't trust the rest * of the VPD. */ - pci_printf(cfg, "bad keyword length: %d\n", - dflen); + pci_printf(cfg, "invalid VPD RV record"); cksumvalid = 0; state = -1; break; @@ -1325,9 +1350,14 @@ pci_read_vpd(device_t pcib, pcicfgregs *cfg) state = -1; break; } + + if (cfg->vpd.vpd_ident == NULL || cfg->vpd.vpd_ident[0] == '\0') { + pci_printf(cfg, "no valid vpd ident found\n"); + state = -2; + } } - if (cksumvalid == 0 || state < -1) { + if (cksumvalid <= 0 || state < -1) { /* read-only data bad, clean up */ if (cfg->vpd.vpd_ros != NULL) { for (off = 0; cfg->vpd.vpd_ros[off].value; off++) diff --git a/sys/dev/pci/pci_user.c b/sys/dev/pci/pci_user.c index 544cb83ece69..fc84b5cfaa25 100644 --- a/sys/dev/pci/pci_user.c +++ b/sys/dev/pci/pci_user.c @@ -562,7 +562,7 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio) { struct pci_vpd_element vpd_element, *vpd_user; struct pcicfg_vpd *vpd; - size_t len; + size_t len, datalen; int error, i; vpd = pci_fetch_vpd_list(dev); @@ -593,16 +593,17 @@ pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio) * Copyout the identifier string followed by each keyword and * value. */ + datalen = strlen(vpd->vpd_ident); + KASSERT(datalen <= 255, ("invalid VPD ident length")); vpd_user = lvio->plvi_data; vpd_element.pve_keyword[0] = '\0'; vpd_element.pve_keyword[1] = '\0'; vpd_element.pve_flags = PVE_FLAG_IDENT; - vpd_element.pve_datalen = strlen(vpd->vpd_ident); + vpd_element.pve_datalen = datalen; error = copyout(&vpd_element, vpd_user, sizeof(vpd_element)); if (error) return (error); - error = copyout(vpd->vpd_ident, vpd_user->pve_data, - strlen(vpd->vpd_ident)); + error = copyout(vpd->vpd_ident, vpd_user->pve_data, datalen); if (error) return (error); vpd_user = PVE_NEXT_LEN(vpd_user, vpd_element.pve_datalen);