Received: by 2002:a25:c205:0:0:0:0:0 with SMTP id s5csp5443744ybf; Thu, 5 Mar 2020 00:47:31 -0800 (PST) X-Google-Smtp-Source: ADFU+vtSoTz/hNT6wTWigy3gImqu5KcKhsNOxJq8YTKnFGq7ZkWAnj9IIxm4mtUEmm83KSM+5MGD X-Received: by 2002:aca:4a4b:: with SMTP id x72mr3512865oia.86.1583398051372; Thu, 05 Mar 2020 00:47:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1583398051; cv=none; d=google.com; s=arc-20160816; b=ylbSzq/sRfMMTIGswzIQDaVn3rkfFagW4iSkGuEPRKzXnx+vWPMTkK3UWDeDqbR6rT 8nICxdlYNDarrTWDpt7KWDNsvUsDFpepdWt2zJDso1E2pOpzRm5En1fhgIxWcRgVcluE M6p8Vzy8ej8m1I3r5HDVSrFRgxtMbI/jxzrqZ3a2AcKBTYG31aPd2g0ZOM1y1YTWV3Qd lDEG5OggqDfEN1n169YQyZnvS2rhP/85xmwXP0MzLYDk5iJRB86ttOTBP5UKJ7ruMfUU oZGGsY4bVsPXV53DM5NFXjjJFtDL+5LZPs153txFsZVIO8s7cs7AEr7Czsr5A6yUoIrM 0V8A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=plB5S/Yn453X4Wx9de0Y/ZZlThGF13RkFYrkchimfDs=; b=OejSJ18f9O3Vfnu8UcEdJaE3sX6SP7mi2fLyetNqM+ZabYcfo23CS7d+o5YPVjWRC9 IOG1O0JLdUSGMW5LUuzha2sXa5lFHilYQoOfodA/dZkpo15Dq7HGzBCrCOXvZDpznvaO Ab0kZjdHOaBC2N6jfKFg+yt0bVcwYRqKCbw6gXdJONqETRJtOM7H27EGJR40Qdrh4ePG SpLOAy/93cvNCxuryqR61qpo3qLZnjga95Sw8N+aEVvY8P04AZ+JiDlXo10jjIwGWrkF pY6qrxRSxGcMJtvgvcZET36Y9WtRg8Ddz7cL8fh91N/z3zcT1O/I4OwpnN6V4Q9hZFlW j8Sw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="JiYovA/q"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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. [209.132.180.67]) by mx.google.com with ESMTP id s128si2967697oig.204.2020.03.05.00.47.19; Thu, 05 Mar 2020 00:47:31 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b="JiYovA/q"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 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 S1726177AbgCEIpt (ORCPT + 99 others); Thu, 5 Mar 2020 03:45:49 -0500 Received: from mail.kernel.org ([198.145.29.99]:41772 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725937AbgCEIpt (ORCPT ); Thu, 5 Mar 2020 03:45:49 -0500 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (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 398B121739 for ; Thu, 5 Mar 2020 08:45:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1583397948; bh=fWS3WZ5Kortv8WPPLoSO915bYycttqhtNLWBFTUiEyk=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=JiYovA/qdm9/SWizxKOMRvJENTYZpYNV6OuvwMqZO4Jja/qXWhpAuiDDN8OHyMPp6 nDsAo5ffUvcwy1P2EOz1oytoMWcMZVLCfc33YAcotxvTiQxc1ryQ1a1FP3u1X4CDZf qRvR4py3rRVhoP+nmgCWKpxeGbvCj50n+JmYIKsA= Received: by mail-wr1-f46.google.com with SMTP id v11so3958660wrm.9 for ; Thu, 05 Mar 2020 00:45:48 -0800 (PST) X-Gm-Message-State: ANhLgQ3HUCELEPd4ZPudww+ldYHCIqpIGJ65oWB+GnTf6QTfGF2buQUK RGxSxj/J94IZKFqkjX/3lTCNVtF52pV2WGfZTrW5wQ== X-Received: by 2002:a5d:6051:: with SMTP id j17mr8872658wrt.151.1583397946565; Thu, 05 Mar 2020 00:45:46 -0800 (PST) MIME-Version: 1.0 References: <20200305084041.24053-1-vdronov@redhat.com> <20200305084041.24053-2-vdronov@redhat.com> In-Reply-To: <20200305084041.24053-2-vdronov@redhat.com> From: Ard Biesheuvel Date: Thu, 5 Mar 2020 09:45:35 +0100 X-Gmail-Original-Message-ID: Message-ID: Subject: Re: [PATCH v3 1/3] efi: fix a race and a buffer overflow while reading efivars via sysfs To: Vladis Dronov Cc: linux-efi , joeyli , Linux Kernel Mailing List Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, 5 Mar 2020 at 09:41, Vladis Dronov wrote: > > There is a race and a buffer overflow corrupting a kernel memory while > reading an efi variable with a size more than 1024 bytes via the older > sysfs method. This happens because accessing struct efi_variable in > efivar_{attr,size,data}_read() and friends is not protected from > a concurrent access leading to a kernel memory corruption and, at best, > to a crash. The race scenario is the following: > > CPU0: CPU1: > efivar_attr_read() > var->DataSize = 1024; > efivar_entry_get(... &var->DataSize) > down_interruptible(&efivars_lock) > efivar_attr_read() // same efi var > var->DataSize = 1024; > efivar_entry_get(... &var->DataSize) > down_interruptible(&efivars_lock) > virt_efi_get_variable() > // returns EFI_BUFFER_TOO_SMALL but > // var->DataSize is set to a real > // var size more than 1024 bytes > up(&efivars_lock) > virt_efi_get_variable() > // called with var->DataSize set > // to a real var size, returns > // successfully and overwrites > // a 1024-bytes kernel buffer > up(&efivars_lock) > > This can be reproduced by concurrent reading of an efi variable which size > is more than 1024 bytes: > > ts# for cpu in $(seq 0 $(nproc --ignore=1)); do ( taskset -c $cpu \ > cat /sys/firmware/efi/vars/KEKDefault*/size & ) ; done > > Fix this by using a local variable for a var's data buffer size so it > does not get overwritten. > > Reported-by: Bob Sanders and the LTP testsuite > Link: https://lore.kernel.org/linux-efi/20200303085528.27658-1-vdronov@redhat.com/T/#u For the future, please don't add these links. This one points to the old version of the patch, not to this one. It will be added by the tooling once the patch gets picked up. > Signed-off-by: Vladis Dronov > --- > drivers/firmware/efi/efivars.c | 29 ++++++++++++++++++++--------- > 1 file changed, 20 insertions(+), 9 deletions(-) > > diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c > index 7576450c8254..69f13bc4b931 100644 > --- a/drivers/firmware/efi/efivars.c > +++ b/drivers/firmware/efi/efivars.c > @@ -83,13 +83,16 @@ static ssize_t > efivar_attr_read(struct efivar_entry *entry, char *buf) > { > struct efi_variable *var = &entry->var; > + unsigned long size = sizeof(var->Data); > char *str = buf; > + int ret; > > if (!entry || !buf) > return -EINVAL; > > - var->DataSize = 1024; > - if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data)) > + ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); > + var->DataSize = size; > + if (ret) > return -EIO; > > if (var->Attributes & EFI_VARIABLE_NON_VOLATILE) > @@ -116,13 +119,16 @@ static ssize_t > efivar_size_read(struct efivar_entry *entry, char *buf) > { > struct efi_variable *var = &entry->var; > + unsigned long size = sizeof(var->Data); > char *str = buf; > + int ret; > > if (!entry || !buf) > return -EINVAL; > > - var->DataSize = 1024; > - if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data)) > + ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); > + var->DataSize = size; > + if (ret) > return -EIO; > > str += sprintf(str, "0x%lx\n", var->DataSize); > @@ -133,12 +139,15 @@ static ssize_t > efivar_data_read(struct efivar_entry *entry, char *buf) > { > struct efi_variable *var = &entry->var; > + unsigned long size = sizeof(var->Data); > + int ret; > > if (!entry || !buf) > return -EINVAL; > > - var->DataSize = 1024; > - if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data)) > + ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); > + var->DataSize = size; > + if (ret) > return -EIO; > > memcpy(buf, var->Data, var->DataSize); > @@ -250,14 +259,16 @@ efivar_show_raw(struct efivar_entry *entry, char *buf) > { > struct efi_variable *var = &entry->var; > struct compat_efi_variable *compat; > + unsigned long datasize = sizeof(var->Data); > size_t size; > + int ret; > > if (!entry || !buf) > return 0; > > - var->DataSize = 1024; > - if (efivar_entry_get(entry, &entry->var.Attributes, > - &entry->var.DataSize, entry->var.Data)) > + ret = efivar_entry_get(entry, &var->Attributes, &datasize, var->Data); > + var->DataSize = datasize; > + if (ret) > return -EIO; > > if (in_compat_syscall()) { > -- > 2.20.1 >