Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp2408119rwd; Fri, 2 Jun 2023 09:01:48 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5QlVRNdkaYmC9x+Xhadi5hQ0NL/wXNluCjaxcP2iq0rfOcCkKzPwoJRrpNKrbY1hwBQbGP X-Received: by 2002:a05:6a20:394c:b0:101:5192:18a9 with SMTP id r12-20020a056a20394c00b00101519218a9mr9733309pzg.21.1685721707957; Fri, 02 Jun 2023 09:01:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1685721707; cv=none; d=google.com; s=arc-20160816; b=gmbt7LVvbf5X5RQyAu6lq9gXZLOA7k+7bNWRBZUakxp9Yz8j6z7u5PjjJaNItXyzPI RfhltCpuUtYY81pC68HrMlyG9LafkvHgeg1O4LTMjZFpSqxVbXJU8RwHAUgPg2GbbF/6 HMLuQFZHf4bheBZV1Kr9JMrvgsElds/Yq1GVXrfornMDnGCDeu9BdwG8NxdD+bpdnOV2 +unORYsVAsnGQmUjGtIqT9nGxdJZPHeblrqBaxRyov6oistNLw96oDGDtKxLJ4rJxzX2 ZcmQP/PIeol0+6ggHAfswR0l1Dt/jMyBHNsYxoe28LIo2/ZN2L2iRasaPmfJ+cjboASr kWoQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:cc:to:subject :message-id:date:from:in-reply-to:references:mime-version :dkim-signature; bh=XVAhse4BY3eKaha4TswgDkvPwrD/95UarSkx0ikeHJo=; b=exGquvxWpTaTV3u6VDO2n9+BX3SsOGOY9c49rce+9K6xfQz2iAjN+eKEymsH2SWziJ 1yMiefe+xNoKzjELCK92f6HkhhCqSYII4BF/25m32qEOTt38mhIb7kPF5gf0WV+TDdLT +i/eO/A7QqJ5SkC+pqzTo2ARmZiN9NXGWSO8WesmfA77Rp9B4l32JJEF1fXzes2/rhvo AHcDZlX/qFRZsYu2v6IcImR6AmnrEog6GP/f1NSEyFLTRCGAlFV5Vks++96K2Wonbezu Shy6bl+GKVL8OHC+VnG2YCinTa1oQueKBqlaMl1u+ri5WKajtD3yaoyErT1ImBgRgKUc DD7g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b="Q/sEsb2p"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id i13-20020a636d0d000000b005348549fae5si1220772pgc.252.2023.06.02.09.01.10; Fri, 02 Jun 2023 09:01:47 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b="Q/sEsb2p"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236219AbjFBPbf (ORCPT + 99 others); Fri, 2 Jun 2023 11:31:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45866 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235457AbjFBPbe (ORCPT ); Fri, 2 Jun 2023 11:31:34 -0400 Received: from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com [IPv6:2607:f8b0:4864:20::1035]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B00A518D; Fri, 2 Jun 2023 08:31:32 -0700 (PDT) Received: by mail-pj1-x1035.google.com with SMTP id 98e67ed59e1d1-2563a4b6285so1090402a91.2; Fri, 02 Jun 2023 08:31:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1685719892; x=1688311892; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=XVAhse4BY3eKaha4TswgDkvPwrD/95UarSkx0ikeHJo=; b=Q/sEsb2pvK1C0fTBXbmLQeHyOvJhNDYHCchkHHSe0G1w3zrDqpF8G8y86kyAE+aHRH sREClAqHg8vksuyG5ig/heKo/sIzu17yMmTYu9wXZoH80oBgl6wHdzqxfWC9D7jhLaZX infND7AifadESe1DfExi6fdKd1zU400z8WQZKJuUb8wlRFgYgNKrXzSBzJsTVVsOPUin 43uahjTZ0ZK5WhC8a+NW70qq6tuJTiN80VtsL2CrjwnsPJDcApswMeCP7gNIKZCyREng twQINvyEPDly6vgI20cDqHe5xUv7BI/5AP9Jf/inG9yFr0XyYlyLp42/vCYu+c0OHUc6 7Sww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685719892; x=1688311892; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XVAhse4BY3eKaha4TswgDkvPwrD/95UarSkx0ikeHJo=; b=NeVDUWfdXkuBMCEhEW5C9BEC4WKzgSxtgW+C/zzNO51c6UUTfwMC8q0/Tc21PUyDdk LzpgVsKGOs7iiGwCdozmwJMmTuw9/d8/lJcev4j9rd+knSANOiagQelnBZHsDaCbDAU9 08WxL6oUCnxqjqROlUUHMtn8djlmNJcTAx7dgsF8q69T76AKa5Edqzr83llVULL+ZSRK cqcSn351+GYy6kheBZnHFI8eTSlWjUNt+Dp8DxFtBjYsFzLlYauufNbtYXNRAQpPzpAr UHh8046EHg2QbmFMIi0CpYzgT/911X+sSXJedqHyc3kaeVsXezA020RucUNwj0aC+rf+ EFcw== X-Gm-Message-State: AC+VfDxZuS+IeLUkeznG2TdjlvYXPioi2NYOwnCo8OCvaYdRy+kEG36P XKUpwVNBUpC7mX+ebP1O14ZQX0FRIp7CD/Q2YBnfhZT5 X-Received: by 2002:a17:90a:19c4:b0:255:7d50:c1aa with SMTP id 4-20020a17090a19c400b002557d50c1aamr170735pjj.44.1685719891711; Fri, 02 Jun 2023 08:31:31 -0700 (PDT) MIME-Version: 1.0 References: <20230601112839.13799-1-dinghui@sangfor.com.cn> <135a45b2c388fbaf9db4620cb01b95230709b9ac.camel@gmail.com> In-Reply-To: From: Alexander Duyck Date: Fri, 2 Jun 2023 08:30:54 -0700 Message-ID: Subject: Re: [PATCH net-next] net: ethtool: Fix out-of-bounds copy to user To: Ding Hui Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, pengdonglin@sangfor.com.cn, huangcun@sangfor.com.cn Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Jun 1, 2023 at 6:46=E2=80=AFPM Ding Hui wr= ote: > > On 2023/6/1 23:04, Alexander H Duyck wrote: > > On Thu, 2023-06-01 at 19:28 +0800, Ding Hui wrote: > >> When we get statistics by ethtool during changing the number of NIC > >> channels greater, the utility may crash due to memory corruption. > >> > >> The NIC drivers callback get_sset_count() could return a calculated > >> length depends on current number of channels (e.g. i40e, igb). > >> > > > > The drivers shouldn't be changing that value. If the drivers are doing > > this they should be fixed to provide a fixed length in terms of their > > strings. > > > > Is there an explicit declaration for the rule? > I searched the ethernet drivers, found that many drivers that support > multiple queues return calculated length by number of queues rather than > fixed value. So pushing all these drivers to follow the rule is hard > to me. > > >> The ethtool allocates a user buffer with the first ioctl returned > >> length and invokes the second ioctl to get data. The kernel copies > >> data to the user buffer but without checking its length. If the length > >> returned by the second get_sset_count() is greater than the length > >> allocated by the user, it will lead to an out-of-bounds copy. > >> > >> Fix it by restricting the copy length not exceed the buffer length > >> specified by userspace. > >> > >> Signed-off-by: Ding Hui > > > > Changing the copy size would not fix this. The problem is the driver > > will be overwriting with the size that it thinks it should be using. > > Reducing the value that is provided for the memory allocations will > > cause the driver to corrupt memory. > > > > I noticed that, in fact I did use the returned length to allocate > kernel memory, and only use adjusted length to copy to user. Ah, okay I hadn't noticed that part. Although that leads me to the question of if any of the drivers might be modifying the length values stored in the structures. We may want to add a new stack variable to track what the clamped value is for these rather than just leaving the value stored in the structure. > >> --- > >> net/ethtool/ioctl.c | 16 +++++++++------- > >> 1 file changed, 9 insertions(+), 7 deletions(-) > >> > >> diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c > >> index 6bb778e10461..82a975a9c895 100644 > >> --- a/net/ethtool/ioctl.c > >> +++ b/net/ethtool/ioctl.c > >> @@ -1902,7 +1902,7 @@ static int ethtool_self_test(struct net_device *= dev, char __user *useraddr) > >> if (copy_from_user(&test, useraddr, sizeof(test))) > >> return -EFAULT; > >> > >> - test.len =3D test_len; > >> + test.len =3D min_t(u32, test.len, test_len); > >> data =3D kcalloc(test_len, sizeof(u64), GFP_USER); > >> if (!data) > >> return -ENOMEM; > > > > This is the wrong spot to be doing this. You need to use test_len for > > your allocation as that is what the driver will be writing to. You > > should look at adjusting after the allocation call and before you do > > the copy > > data =3D kcalloc(test_len, sizeof(u64), GFP_USER); // yes, **test_len** = for kernel memory > ... > copy_to_user(useraddr, data, array_size(test.len, sizeof(u64)) // **test.= len** only for copy to user One other thought on this. Would we ever expect the length value to change? For many of these I wonder if we shouldn't just return an error in the case that there isn't enough space to store the test results. It might make sense to look at adding a return of ENOSPC or EFBIG when we encounter a size difference where our output is too big for the provided userspace buffer. At least with that we are not losing data. > > > >> @@ -1915,7 +1915,8 @@ static int ethtool_self_test(struct net_device *= dev, char __user *useraddr) > >> if (copy_to_user(useraddr, &test, sizeof(test))) > >> goto out; > >> useraddr +=3D sizeof(test); > >> - if (copy_to_user(useraddr, data, array_size(test.len, sizeof(u64)= ))) > >> + if (test.len && > >> + copy_to_user(useraddr, data, array_size(test.len, sizeof(u64)= ))) > >> goto out; > >> ret =3D 0; > >> > > > > I don't believe this is adding any value. I wouldn't bother with > > checking for lengths of 0. > > > > Yes, we already checked the data ptr is not NULL, so we don't need checki= ng test.len. > I'll remove it in v2. > > >> @@ -1940,10 +1941,10 @@ static int ethtool_get_strings(struct net_devi= ce *dev, void __user *useraddr) > >> return -ENOMEM; > >> WARN_ON_ONCE(!ret); > >> > >> - gstrings.len =3D ret; > >> + gstrings.len =3D min_t(u32, gstrings.len, ret); > >> > >> if (gstrings.len) { > >> - data =3D vzalloc(array_size(gstrings.len, ETH_GSTRING_LEN= )); > >> + data =3D vzalloc(array_size(ret, ETH_GSTRING_LEN)); > >> if (!data) > >> return -ENOMEM; > >> > > > > Same here. We should be using the returned value for the allocations > > and tests, and then doing the min adjustment after the allocationis > > completed. > > > > gstrings.len =3D min_t(u32, gstrings.len, ret); // adjusting > > if (gstrings.len) { // checking the adjusted gstrings.len can avoid unne= cessary vzalloc and __ethtool_get_strings() > vzalloc(array_size(ret, ETH_GSTRING_LEN)); // **ret** for kernel memory, = rather than adjusted lenght > > At last, adjusted gstrings.len for copy to user I see what you are talking about now. > >> @@ -2055,9 +2056,9 @@ static int ethtool_get_stats(struct net_device *= dev, void __user *useraddr) > >> if (copy_from_user(&stats, useraddr, sizeof(stats))) > >> return -EFAULT; > >> > >> - stats.n_stats =3D n_stats; > >> + stats.n_stats =3D min_t(u32, stats.n_stats, n_stats); > >> > >> - if (n_stats) { > >> + if (stats.n_stats) { > >> data =3D vzalloc(array_size(n_stats, sizeof(u64))); > >> if (!data) > >> return -ENOMEM; > > > > Same here. We should be using n_stats, not stats.n_stats and adjust > > before you do the final copy. > > > >> @@ -2070,7 +2071,8 @@ static int ethtool_get_stats(struct net_device *= dev, void __user *useraddr) > >> if (copy_to_user(useraddr, &stats, sizeof(stats))) > >> goto out; > >> useraddr +=3D sizeof(stats); > >> - if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, s= izeof(u64)))) > >> + if (stats.n_stats && > >> + copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof= (u64)))) > >> goto out; > >> ret =3D 0; > >> > > > > Again. I am not sure what value is being added. If n_stats is 0 then I > > am pretty sure this will do nothing anyway. > > > > Not really no, n_stats is returned value, and stats.n_stats is adjusted v= alue, > if the adjusted stats.n_stats is 0, we avoid memory allocation and settin= g data ptr > to NULL, copy_to_user() with NULL ptr maybe cause warn log. I see now. So data is NULL if stats.n_stats is 0.