Received: by 2002:a25:e7d8:0:0:0:0:0 with SMTP id e207csp1658226ybh; Sun, 8 Mar 2020 09:39:42 -0700 (PDT) X-Google-Smtp-Source: ADFU+vs4kbxn1O5t7Vd+Yn6uGCzzMPAlCTP6Erwy/gxIr3ZAlR2szv50SDiDM98zm7P4rDQK2vxK X-Received: by 2002:aca:c596:: with SMTP id v144mr1411979oif.136.1583685582317; Sun, 08 Mar 2020 09:39:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1583685582; cv=none; d=google.com; s=arc-20160816; b=a7UVvMIQtG9XXayY0I/WeCxfU+s6iSVcZjVW7zs7Iz7Jr6HG1Psd/2YN9B9iO8ZaY7 PyXOQhtoogC2T3oWMrhmnE0/PVhf0DL4uR12NWsXS670Werq3SjPD2fInJMFUNASBpAr /UVa+HKjEp4vJSVrgzomxMUZEd2PHlZZWle6MP6Wt0oY1TmDQ7uW4EwDECrbwe4rZSF8 cakzmUbj9aEBFDCR2RhvRVLpoj5X7uSJAhNV8RyX4E64vvFKpMSeNtm2iJ0QvZsN8gH3 SqpMcAzu9frOnqoxfepiqC0xZ+SL0/ZYgUFWbZAPieMjz1ySnBYc6Pc1tt8YpUjUq6G0 l1iQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :robot-unsubscribe:robot-id:message-id:mime-version:references :in-reply-to:cc:subject:to:reply-to:from:date; bh=bszMsrbsnfByGNFnqYi8t3AEXocYUjJFz/dQfq4+0Hw=; b=txP92ZS/tiPe7cBQVx3Tt2TNssKnlCqkmAvS7GniTnuWA1F9CXFSk1vgXbAQ2WCwfZ AW6ZjHUdFO3F8tJ3foPspd9rTZxsdXxtOYp4rl3oDIXli7F3A4kvfu//3EnsuImpCjW3 RhEMt3odDLdzIfjNRRX7viBBQGh+yi67GP6ePnkf7k6pyoga5Fx3boy15zmTdfUQf+MB Xuem7S/EV6uCDK1f2Cfd5syyS4R0EUDXcOGT3223nJMfk8P990pAOC7oorsGfUQrPCXV BkpQI8hGFF5LxMN0ISX3A9MkN/bdmnEpiLo1CL5mn11bmGROp82A3HpdT0/P924BYbvy md1g== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id k24si1085563otf.213.2020.03.08.09.39.30; Sun, 08 Mar 2020 09:39:42 -0700 (PDT) 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; 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 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726385AbgCHQio (ORCPT + 99 others); Sun, 8 Mar 2020 12:38:44 -0400 Received: from Galois.linutronix.de ([193.142.43.55]:56802 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726259AbgCHQio (ORCPT ); Sun, 8 Mar 2020 12:38:44 -0400 Received: from [5.158.153.53] (helo=tip-bot2.lab.linutronix.de) by Galois.linutronix.de with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1jAywj-0007V2-NC; Sun, 08 Mar 2020 17:38:33 +0100 Received: from [127.0.1.1] (localhost [IPv6:::1]) by tip-bot2.lab.linutronix.de (Postfix) with ESMTP id 5E1351C221D; Sun, 8 Mar 2020 17:38:33 +0100 (CET) Date: Sun, 08 Mar 2020 16:38:33 -0000 From: "tip-bot2 for Vladis Dronov" Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: efi/urgent] efi: Fix a race and a buffer overflow while reading efivars via sysfs Cc: Bob Sanders , Vladis Dronov , Ard Biesheuvel , Ingo Molnar , , x86 , LKML In-Reply-To: <20200305084041.24053-2-vdronov@redhat.com> References: <20200305084041.24053-2-vdronov@redhat.com> MIME-Version: 1.0 Message-ID: <158368551309.28353.6521114137797916495.tip-bot2@tip-bot2> X-Mailer: tip-git-log-daemon Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The following commit has been merged into the efi/urgent branch of tip: Commit-ID: 286d3250c9d6437340203fb64938bea344729a0e Gitweb: https://git.kernel.org/tip/286d3250c9d6437340203fb64938bea344729a0e Author: Vladis Dronov AuthorDate: Sun, 08 Mar 2020 09:08:54 +01:00 Committer: Ingo Molnar CommitterDate: Sun, 08 Mar 2020 09:56:34 +01:00 efi: Fix a race and a buffer overflow while reading efivars via sysfs 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. Fixes: e14ab23dde12b80d ("efivars: efivar_entry API") Reported-by: Bob Sanders and the LTP testsuite Signed-off-by: Vladis Dronov Signed-off-by: Ard Biesheuvel Signed-off-by: Ingo Molnar Cc: Link: https://lore.kernel.org/r/20200305084041.24053-2-vdronov@redhat.com Link: https://lore.kernel.org/r/20200308080859.21568-24-ardb@kernel.org --- 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 7576450..69f13bc 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()) {