Message ID | 20231016212507.131902-1-slyich@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2908:b0:403:3b70:6f57 with SMTP id ib8csp3732977vqb; Mon, 16 Oct 2023 14:26:09 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFYb8s1RDihdY3lpbdVCVD1AiwqZWCYn1AYbwf5VoFyICtzjEuQq4B8uwuV9TfqsTE5xBxT X-Received: by 2002:a17:902:d04c:b0:1b8:2ba0:c9a8 with SMTP id l12-20020a170902d04c00b001b82ba0c9a8mr498979pll.2.1697491569283; Mon, 16 Oct 2023 14:26:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1697491569; cv=none; d=google.com; s=arc-20160816; b=oYKGh8E0ggwb+8tW5FBqi3hfTpfovRg2xNiyMOvaeyEyv7GUg0ZtitD8HC1ZFh3IqL dfF+46BZnCNhN1pPPfG89hslZxyqeIKqVHzYfdM3ZjSkWk4NIswaH2y2xvI0kAwhy7lg W0lqreSshREM47Gy9okLkZJAkUT4A/apPbU+LOSPb+E1z0cArYwPRike03wHfEWBV0/v r4+G9IYJkmcUW0uMgIlQKa7QpJLsU4veyQBP6ASN6yoZ/2u5lmq/5/K/BiSwqEviOUqB bTKS30YsLHxzkV2fzC2gjVck3sf9LtOlI8SsCc9vuYgv4uCu3IMao3+dCuWR1jeMbu67 zaRA== 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 :message-id:date:subject:cc:to:from:dkim-signature; bh=VhbRGW1BROKWRFNldIDINnruhAZTrbVrzlHubFel+Hc=; fh=3HYTwr77GOXCi1IyeFjq3jMSMH3SlefkQZa3J3DiG0Q=; b=GeHlUTTkW5Y1RdXeCWqTkqsVRsU6ShwQApfnZJUiO2Lr0FoBPthEw0gGVEjaFLH9Mf RregJHPRSNgbn3M2IsSOUcApa7qS2cWNKL99QQ7+QOjKMPTpK5drBNbmwAvTmBcKW/v3 mhDeK76+WOUvLWY0AmUGntvm9rA8QIKMJgWYHSW3Rv+Xr8B/uEQNf97Fqz29hMqWmx8/ +X+/qfSjBBupzCXMI9pxAnhoKyvBiYDXo0ywC+pSItbsSrITASAzYhOBWAlBW/MZvXUd nzL1F1x15QYVJOm4U7yBo6BPQGtMQiCxvCq9BTgMKAbSW8wVlobO9zIxDmQN/ItItHm5 G8qg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=LTmA3Uv6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from fry.vger.email (fry.vger.email. [23.128.96.38]) by mx.google.com with ESMTPS id n16-20020a170902d2d000b001c1e4f9c63esi175002plc.491.2023.10.16.14.26.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Oct 2023 14:26:09 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) client-ip=23.128.96.38; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=LTmA3Uv6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.38 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by fry.vger.email (Postfix) with ESMTP id 4E106807E454; Mon, 16 Oct 2023 14:25:40 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at fry.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232577AbjJPVZU (ORCPT <rfc822;hjfbswb@gmail.com> + 18 others); Mon, 16 Oct 2023 17:25:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229666AbjJPVZT (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Mon, 16 Oct 2023 17:25:19 -0400 Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8637AA2 for <linux-kernel@vger.kernel.org>; Mon, 16 Oct 2023 14:25:17 -0700 (PDT) Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-40776b20031so27205485e9.0 for <linux-kernel@vger.kernel.org>; Mon, 16 Oct 2023 14:25:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697491516; x=1698096316; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=VhbRGW1BROKWRFNldIDINnruhAZTrbVrzlHubFel+Hc=; b=LTmA3Uv6jthiKo78TwukFJKONbYJRuqxoLDO276hNxSY6wB27kQrMKGJvrjQLCmu2g pgoXRQe1dPJhZNwNS2tjgaGqW32EcoMulyYANasLQOD96RUljQQhShshfpLX2ebKMNxP X7oLl5T307nRf159lwZ/f4TQp8nvR9ItdydXMGYJvfHJEveBMs6VlYOmgDnE2mjOb6t7 xDVTl0nc4n3CxIOZc65zLZQVEZiqs7yf0B43K0USxRO1gmqJSa46ja0skRyXYyXWd5KV aT+8B7VJcMaytPXgEvCQKEewEd3ajQCPFbpyYGhtFTzWj0MpN2DsGcqYosALtwdUcp7z Xikg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697491516; x=1698096316; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=VhbRGW1BROKWRFNldIDINnruhAZTrbVrzlHubFel+Hc=; b=MFUUingTbo/J3jLYUsljAAUoDDkl/QwhVYQuVWUn+QAcoz8DS9zQd7s7Q6y/H8R8gv UHCnSQpbX+k4TndWUlMYT9ria5BwD0fJsqWV56Kj2L+EZYSIHJCCrEOig7J2q+FAA0Op +ydA6mPl2hs4/geOQngMumRzmJR2iYt0mN6aezyc9nVSwDQ43Pa8+H77WgQMy+lJOGxl 0JQvVMnVzTNPGDNqjQExGuDh/nSrsT1EY9Tm1sFNSkiTQrkYnxu9C/4pLDWotwRPccnD MS5l5qKrbZpeOjR5adeRPoSDc8vWP9ArK1vlloKiqvDaLXsAi1wWGkehVQGB+tBH5Mqn 8K1w== X-Gm-Message-State: AOJu0YwShgDCnyrf966KSQJ3g3/Eh4w4gvf4scRiUCsTbV4ARkKKdkeG 5Z6Qt1WN94w2yvzSdVLxk4I= X-Received: by 2002:a05:600c:a48:b0:3fe:2a98:a24c with SMTP id c8-20020a05600c0a4800b003fe2a98a24cmr242150wmq.26.1697491515524; Mon, 16 Oct 2023 14:25:15 -0700 (PDT) Received: from nz.home (host86-139-202-110.range86-139.btcentralplus.com. [86.139.202.110]) by smtp.gmail.com with ESMTPSA id k21-20020a05600c1c9500b0040607da271asm8229381wms.31.2023.10.16.14.25.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Oct 2023 14:25:14 -0700 (PDT) Received: by nz.home (Postfix, from userid 1000) id 3CD2111AC29807; Mon, 16 Oct 2023 22:25:14 +0100 (BST) From: Sergei Trofimovich <slyich@gmail.com> To: linux-kernel@vger.kernel.org Cc: Sergei Trofimovich <slyich@gmail.com>, Andrew Morton <akpm@linux-foundation.org>, Eric Biederman <ebiederm@xmission.com>, Kees Cook <keescook@chromium.org>, linux-mm@kvack.org Subject: [RESEND PATCH] uapi: increase MAX_ARG_STRLEN from 128K to 6M Date: Mon, 16 Oct 2023 22:25:07 +0100 Message-ID: <20231016212507.131902-1-slyich@gmail.com> X-Mailer: git-send-email 2.42.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.6 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,SPF_HELO_NONE, SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on fry.vger.email Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (fry.vger.email [0.0.0.0]); Mon, 16 Oct 2023 14:25:40 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1779948919651975284 X-GMAIL-MSGID: 1779948919651975284 |
Series |
[RESEND] uapi: increase MAX_ARG_STRLEN from 128K to 6M
|
|
Commit Message
Sergei Trofimovich
Oct. 16, 2023, 9:25 p.m. UTC
Before the change linux allowed individual execve() arguments or
environment variable entries to be only as big as 32 pages.
Histroically before b6a2fea3931 "mm: variable length argument support"
MAX_ARG_STRLEN used to be full allowed size `argv[] + envp[]`.
When full limit was abandoned individual parameters were still limited
by a safe limit of 128K.
Nowadays' linux allows `argv[]+envp[]` to be as laerge as 6MB (3/4
`_STK_LIM`).
Some build systems like `autoconf` use a single environment variable
to pass `CFLAGS` environment variable around. It's not a bug problem
if the argument list is short.
But some packaging systems prefer installing each package into
individual directory. As a result that requires quite long string of
parameters like:
CFLAGS="-I/path/to/pkg1 -I/path/to/pkg2 ..."
This can easily overflow 128K and does happen for `NixOS` and `nixpkgs`
repositories on a regular basis.
Similar pattern is exhibited by `gcc` which converts it's input command
line into a single environment variable (https://gcc.gnu.org/PR111527):
$ big_100k_var=$(printf "%0*d" 100000 0)
# this works: 200KB of options for `printf` external command
$ $(which printf) "%s %s" $big_100k_var $big_100k_var >/dev/null; echo $?
0
# this fails: 200KB of options for `gcc`, fails in `cc1`
$ touch a.c; gcc -c a.c -DA=$big_100k_var -DB=$big_100k_var
gcc: fatal error: cannot execute 'cc1': execv: Argument list too long
compilation terminated.
I would say this 128KB limitation is arbitrary.
The change raises the limit of `MAX_ARG_STRLEN` from 32 pakes (128K
n `x86_64`) to the maximum limit of stack allowed by Linux today.
It has a minor chance of overflowing userspace programs that use
`MAX_ARG_STRLEN` to allocate the strings on stack. It should not be a
big problem as such programs are already are at risk of overflowing
stack.
Tested as:
$ V=$(printf "%*d" 1000000 0) ls
Before the change it failed with `ls: Argument list too long`. After the
change `ls` executes as expected.
WDYT of abandoning the limit and allow user to fill entire environment
with a single command or a single variable?
CC: Andrew Morton <akpm@linux-foundation.org>
CC: Eric Biederman <ebiederm@xmission.com>
CC: Kees Cook <keescook@chromium.org>
CC: linux-mm@kvack.org
CC: linux-kernel@vger.kernel.org
Signed-off-by: Sergei Trofimovich <slyich@gmail.com>
---
include/uapi/linux/binfmts.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
Comments
On Mon, Oct 16, 2023 at 10:25:07PM +0100, Sergei Trofimovich wrote: > Before the change linux allowed individual execve() arguments or > environment variable entries to be only as big as 32 pages. > > Histroically before b6a2fea3931 "mm: variable length argument support" > MAX_ARG_STRLEN used to be full allowed size `argv[] + envp[]`. > > When full limit was abandoned individual parameters were still limited > by a safe limit of 128K. > > Nowadays' linux allows `argv[]+envp[]` to be as laerge as 6MB (3/4 > `_STK_LIM`). See bprm_stack_limits() for the details, but yes, 3/4 _STK_LIM tends to be the max, unless the rlim_stack is set smaller. > Some build systems like `autoconf` use a single environment variable > to pass `CFLAGS` environment variable around. It's not a bug problem > if the argument list is short. > > But some packaging systems prefer installing each package into > individual directory. As a result that requires quite long string of > parameters like: > > CFLAGS="-I/path/to/pkg1 -I/path/to/pkg2 ..." > > This can easily overflow 128K and does happen for `NixOS` and `nixpkgs` > repositories on a regular basis. That's ... alarming. What are you doing currently to work around this? > > Similar pattern is exhibited by `gcc` which converts it's input command > line into a single environment variable (https://gcc.gnu.org/PR111527): > > $ big_100k_var=$(printf "%0*d" 100000 0) > > # this works: 200KB of options for `printf` external command > $ $(which printf) "%s %s" $big_100k_var $big_100k_var >/dev/null; echo $? > 0 > > # this fails: 200KB of options for `gcc`, fails in `cc1` > $ touch a.c; gcc -c a.c -DA=$big_100k_var -DB=$big_100k_var > gcc: fatal error: cannot execute 'cc1': execv: Argument list too long > compilation terminated. > > I would say this 128KB limitation is arbitrary. > The change raises the limit of `MAX_ARG_STRLEN` from 32 pakes (128K > n `x86_64`) to the maximum limit of stack allowed by Linux today. > > It has a minor chance of overflowing userspace programs that use > `MAX_ARG_STRLEN` to allocate the strings on stack. It should not be a > big problem as such programs are already are at risk of overflowing > stack. > > Tested as: > $ V=$(printf "%*d" 1000000 0) ls > > Before the change it failed with `ls: Argument list too long`. After the > change `ls` executes as expected. > > WDYT of abandoning the limit and allow user to fill entire environment > with a single command or a single variable? Changing this value shouldn't risk any vma collisions, since exec is still checking the utilization before starting the actual process replacement steps (see bprm->argmin). It does seem pathological to set this to the full 6MB, though, since that would _immediately_ get rejected by execve() with an -E2BIG, but ultimately, there isn't really any specific limit to the length of individual strings as long as the entire space is less than bprm->argmin. Perhaps MAX_ARG_STRLEN should be removed entirely and the kernel can just use bprm->argmin? I haven't really looked at that to see if it's sane, though. It just feels like MAX_ARG_STRLEN isn't a meaningful limit. -Kees > > CC: Andrew Morton <akpm@linux-foundation.org> > CC: Eric Biederman <ebiederm@xmission.com> > CC: Kees Cook <keescook@chromium.org> > CC: linux-mm@kvack.org > CC: linux-kernel@vger.kernel.org > Signed-off-by: Sergei Trofimovich <slyich@gmail.com> > --- > include/uapi/linux/binfmts.h | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/include/uapi/linux/binfmts.h b/include/uapi/linux/binfmts.h > index c6f9450efc12..4e828515a22e 100644 > --- a/include/uapi/linux/binfmts.h > +++ b/include/uapi/linux/binfmts.h > @@ -8,11 +8,11 @@ struct pt_regs; > > /* > * These are the maximum length and maximum number of strings passed to the > - * execve() system call. MAX_ARG_STRLEN is essentially random but serves to > - * prevent the kernel from being unduly impacted by misaddressed pointers. > + * execve() system call. MAX_ARG_STRLEN is as large as Linux allows new > + * stack to grow. Currently it's `_STK_LIM / 4 * 3 = 6MB` (see fs/exec.c). > * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. > */ > -#define MAX_ARG_STRLEN (PAGE_SIZE * 32) > +#define MAX_ARG_STRLEN (6 * 1024 * 1024) > #define MAX_ARG_STRINGS 0x7FFFFFFF > > /* sizeof(linux_binprm->buf) */ > -- > 2.42.0 >
On Mon, 16 Oct 2023 14:50:05 -0700 Kees Cook <keescook@chromium.org> wrote: > On Mon, Oct 16, 2023 at 10:25:07PM +0100, Sergei Trofimovich wrote: > > Before the change linux allowed individual execve() arguments or > > environment variable entries to be only as big as 32 pages. > > > > Histroically before b6a2fea3931 "mm: variable length argument support" > > MAX_ARG_STRLEN used to be full allowed size `argv[] + envp[]`. > > > > When full limit was abandoned individual parameters were still limited > > by a safe limit of 128K. > > > > Nowadays' linux allows `argv[]+envp[]` to be as laerge as 6MB (3/4 > > `_STK_LIM`). > > See bprm_stack_limits() for the details, but yes, 3/4 _STK_LIM tends to > be the max, unless the rlim_stack is set smaller. > > > Some build systems like `autoconf` use a single environment variable > > to pass `CFLAGS` environment variable around. It's not a bug problem > > if the argument list is short. > > > > But some packaging systems prefer installing each package into > > individual directory. As a result that requires quite long string of > > parameters like: > > > > CFLAGS="-I/path/to/pkg1 -I/path/to/pkg2 ..." > > > > This can easily overflow 128K and does happen for `NixOS` and `nixpkgs` > > repositories on a regular basis. > > That's ... alarming. What are you doing currently to work around this? We usually try to stay under the threshold. When the problem arises due to organic growth of inputs over time we either suffer build failures without any reasonable workarounds or if the change was recent and inflated command line options we revert the change and add hacks into other places (like patching `gcc` directly to apply the transformations). Longer term it would be nice for `gcc` to allow unbounded output via response files, but it will take some time to sort out as current flags rewriting expands all flags and response files into a single huge variable and hits the 128K limit: https://gcc.gnu.org/pipermail/gcc/2023-October/242641.html Medium term dropping kernel's arbitrary small limit (this change) sounds like the simplest solution. > > > > Similar pattern is exhibited by `gcc` which converts it's input command > > line into a single environment variable (https://gcc.gnu.org/PR111527): > > > > $ big_100k_var=$(printf "%0*d" 100000 0) > > > > # this works: 200KB of options for `printf` external command > > $ $(which printf) "%s %s" $big_100k_var $big_100k_var >/dev/null; echo $? > > 0 > > > > # this fails: 200KB of options for `gcc`, fails in `cc1` > > $ touch a.c; gcc -c a.c -DA=$big_100k_var -DB=$big_100k_var > > gcc: fatal error: cannot execute 'cc1': execv: Argument list too long > > compilation terminated. > > > > I would say this 128KB limitation is arbitrary. > > The change raises the limit of `MAX_ARG_STRLEN` from 32 pakes (128K > > n `x86_64`) to the maximum limit of stack allowed by Linux today. > > > > It has a minor chance of overflowing userspace programs that use > > `MAX_ARG_STRLEN` to allocate the strings on stack. It should not be a > > big problem as such programs are already are at risk of overflowing > > stack. > > > > Tested as: > > $ V=$(printf "%*d" 1000000 0) ls > > > > Before the change it failed with `ls: Argument list too long`. After the > > change `ls` executes as expected. > > > > WDYT of abandoning the limit and allow user to fill entire environment > > with a single command or a single variable? > > Changing this value shouldn't risk any vma collisions, since exec is > still checking the utilization before starting the actual process > replacement steps (see bprm->argmin). > > It does seem pathological to set this to the full 6MB, though, since > that would _immediately_ get rejected by execve() with an -E2BIG, but > ultimately, there isn't really any specific limit to the length of > individual strings as long as the entire space is less than > bprm->argmin. > > Perhaps MAX_ARG_STRLEN should be removed entirely and the kernel can > just use bprm->argmin? I haven't really looked at that to see if it's > sane, though. It just feels like MAX_ARG_STRLEN isn't a meaningful > limit. Removing the limit entirely in favour of 'bprm->argmin' sounds good. I'll try to make it so and will send v2. What should be the fate of userspace-exported `MAX_ARG_STRLEN` value in that case? Should it stay `(PAGE_SIZE * 32)`? Should it be removed entirely? (a chance of user space build failures). If we are to remove it entirely what should be the reasonable limit in kernel for other subsystems that still use it? fs/binfmt_elf.c: len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); fs/binfmt_elf_fdpic.c: len = strnlen_user(p, MAX_ARG_STRLEN); fs/binfmt_flat.c: len = strnlen_user(p, MAX_ARG_STRLEN); kernel/auditsc.c: len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1; Keeping it at an "arbitrary" 6MB limit looked safe :) > -Kees > > > > > CC: Andrew Morton <akpm@linux-foundation.org> > > CC: Eric Biederman <ebiederm@xmission.com> > > CC: Kees Cook <keescook@chromium.org> > > CC: linux-mm@kvack.org > > CC: linux-kernel@vger.kernel.org > > Signed-off-by: Sergei Trofimovich <slyich@gmail.com> > > --- > > include/uapi/linux/binfmts.h | 6 +++--- > > 1 file changed, 3 insertions(+), 3 deletions(-) > > > > diff --git a/include/uapi/linux/binfmts.h b/include/uapi/linux/binfmts.h > > index c6f9450efc12..4e828515a22e 100644 > > --- a/include/uapi/linux/binfmts.h > > +++ b/include/uapi/linux/binfmts.h > > @@ -8,11 +8,11 @@ struct pt_regs; > > > > /* > > * These are the maximum length and maximum number of strings passed to the > > - * execve() system call. MAX_ARG_STRLEN is essentially random but serves to > > - * prevent the kernel from being unduly impacted by misaddressed pointers. > > + * execve() system call. MAX_ARG_STRLEN is as large as Linux allows new > > + * stack to grow. Currently it's `_STK_LIM / 4 * 3 = 6MB` (see fs/exec.c). > > * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. > > */ > > -#define MAX_ARG_STRLEN (PAGE_SIZE * 32) > > +#define MAX_ARG_STRLEN (6 * 1024 * 1024) > > #define MAX_ARG_STRINGS 0x7FFFFFFF > > > > /* sizeof(linux_binprm->buf) */ > > -- > > 2.42.0 > > > > -- > Kees Cook
On Mon, Oct 16, 2023 at 11:33:18PM +0100, Sergei Trofimovich wrote: > On Mon, 16 Oct 2023 14:50:05 -0700 > Kees Cook <keescook@chromium.org> wrote: > > > On Mon, Oct 16, 2023 at 10:25:07PM +0100, Sergei Trofimovich wrote: > > > Before the change linux allowed individual execve() arguments or > > > environment variable entries to be only as big as 32 pages. > > > > > > Histroically before b6a2fea3931 "mm: variable length argument support" > > > MAX_ARG_STRLEN used to be full allowed size `argv[] + envp[]`. > > > > > > When full limit was abandoned individual parameters were still limited > > > by a safe limit of 128K. > > > > > > Nowadays' linux allows `argv[]+envp[]` to be as laerge as 6MB (3/4 > > > `_STK_LIM`). > > > > See bprm_stack_limits() for the details, but yes, 3/4 _STK_LIM tends to > > be the max, unless the rlim_stack is set smaller. > > > > > Some build systems like `autoconf` use a single environment variable > > > to pass `CFLAGS` environment variable around. It's not a bug problem > > > if the argument list is short. > > > > > > But some packaging systems prefer installing each package into > > > individual directory. As a result that requires quite long string of > > > parameters like: > > > > > > CFLAGS="-I/path/to/pkg1 -I/path/to/pkg2 ..." > > > > > > This can easily overflow 128K and does happen for `NixOS` and `nixpkgs` > > > repositories on a regular basis. > > > > That's ... alarming. What are you doing currently to work around this? > > We usually try to stay under the threshold. When the problem arises due > to organic growth of inputs over time we either suffer build failures > without any reasonable workarounds or if the change was recent and > inflated command line options we revert the change and add hacks into > other places (like patching `gcc` directly to apply the > transformations). > > Longer term it would be nice for `gcc` to allow unbounded output via > response files, but it will take some time to sort out as current flags > rewriting expands all flags and response files into a single huge > variable and hits the 128K limit: > > https://gcc.gnu.org/pipermail/gcc/2023-October/242641.html > > Medium term dropping kernel's arbitrary small limit (this change) sounds > like the simplest solution. > > > > > > > Similar pattern is exhibited by `gcc` which converts it's input command > > > line into a single environment variable (https://gcc.gnu.org/PR111527): > > > > > > $ big_100k_var=$(printf "%0*d" 100000 0) > > > > > > # this works: 200KB of options for `printf` external command > > > $ $(which printf) "%s %s" $big_100k_var $big_100k_var >/dev/null; echo $? > > > 0 > > > > > > # this fails: 200KB of options for `gcc`, fails in `cc1` > > > $ touch a.c; gcc -c a.c -DA=$big_100k_var -DB=$big_100k_var > > > gcc: fatal error: cannot execute 'cc1': execv: Argument list too long > > > compilation terminated. > > > > > > I would say this 128KB limitation is arbitrary. > > > The change raises the limit of `MAX_ARG_STRLEN` from 32 pakes (128K > > > n `x86_64`) to the maximum limit of stack allowed by Linux today. > > > > > > It has a minor chance of overflowing userspace programs that use > > > `MAX_ARG_STRLEN` to allocate the strings on stack. It should not be a > > > big problem as such programs are already are at risk of overflowing > > > stack. > > > > > > Tested as: > > > $ V=$(printf "%*d" 1000000 0) ls > > > > > > Before the change it failed with `ls: Argument list too long`. After the > > > change `ls` executes as expected. > > > > > > WDYT of abandoning the limit and allow user to fill entire environment > > > with a single command or a single variable? > > > > Changing this value shouldn't risk any vma collisions, since exec is > > still checking the utilization before starting the actual process > > replacement steps (see bprm->argmin). > > > > It does seem pathological to set this to the full 6MB, though, since > > that would _immediately_ get rejected by execve() with an -E2BIG, but > > ultimately, there isn't really any specific limit to the length of > > individual strings as long as the entire space is less than > > bprm->argmin. > > > > Perhaps MAX_ARG_STRLEN should be removed entirely and the kernel can > > just use bprm->argmin? I haven't really looked at that to see if it's > > sane, though. It just feels like MAX_ARG_STRLEN isn't a meaningful > > limit. > > Removing the limit entirely in favour of 'bprm->argmin' sounds good. > I'll try to make it so and will send v2. > > What should be the fate of userspace-exported `MAX_ARG_STRLEN` value in > that case? Should it stay `(PAGE_SIZE * 32)`? Should it be removed > entirely? (a chance of user space build failures). > > If we are to remove it entirely what should be the reasonable limit in > kernel for other subsystems that still use it? > > fs/binfmt_elf.c: len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); > fs/binfmt_elf_fdpic.c: len = strnlen_user(p, MAX_ARG_STRLEN); > fs/binfmt_flat.c: len = strnlen_user(p, MAX_ARG_STRLEN); > kernel/auditsc.c: len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1; > > Keeping it at an "arbitrary" 6MB limit looked safe :) Yeah, I think leaving MAX_ARG_STRLEN totally unchanged but adjust where it is used only for the ELF loader is probably the least risky thing to do here. -Kees > > > -Kees > > > > > > > > CC: Andrew Morton <akpm@linux-foundation.org> > > > CC: Eric Biederman <ebiederm@xmission.com> > > > CC: Kees Cook <keescook@chromium.org> > > > CC: linux-mm@kvack.org > > > CC: linux-kernel@vger.kernel.org > > > Signed-off-by: Sergei Trofimovich <slyich@gmail.com> > > > --- > > > include/uapi/linux/binfmts.h | 6 +++--- > > > 1 file changed, 3 insertions(+), 3 deletions(-) > > > > > > diff --git a/include/uapi/linux/binfmts.h b/include/uapi/linux/binfmts.h > > > index c6f9450efc12..4e828515a22e 100644 > > > --- a/include/uapi/linux/binfmts.h > > > +++ b/include/uapi/linux/binfmts.h > > > @@ -8,11 +8,11 @@ struct pt_regs; > > > > > > /* > > > * These are the maximum length and maximum number of strings passed to the > > > - * execve() system call. MAX_ARG_STRLEN is essentially random but serves to > > > - * prevent the kernel from being unduly impacted by misaddressed pointers. > > > + * execve() system call. MAX_ARG_STRLEN is as large as Linux allows new > > > + * stack to grow. Currently it's `_STK_LIM / 4 * 3 = 6MB` (see fs/exec.c). > > > * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. > > > */ > > > -#define MAX_ARG_STRLEN (PAGE_SIZE * 32) > > > +#define MAX_ARG_STRLEN (6 * 1024 * 1024) > > > #define MAX_ARG_STRINGS 0x7FFFFFFF > > > > > > /* sizeof(linux_binprm->buf) */ > > > -- > > > 2.42.0 > > > > > > > -- > > Kees Cook > > > -- > > Sergei
diff --git a/include/uapi/linux/binfmts.h b/include/uapi/linux/binfmts.h index c6f9450efc12..4e828515a22e 100644 --- a/include/uapi/linux/binfmts.h +++ b/include/uapi/linux/binfmts.h @@ -8,11 +8,11 @@ struct pt_regs; /* * These are the maximum length and maximum number of strings passed to the - * execve() system call. MAX_ARG_STRLEN is essentially random but serves to - * prevent the kernel from being unduly impacted by misaddressed pointers. + * execve() system call. MAX_ARG_STRLEN is as large as Linux allows new + * stack to grow. Currently it's `_STK_LIM / 4 * 3 = 6MB` (see fs/exec.c). * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. */ -#define MAX_ARG_STRLEN (PAGE_SIZE * 32) +#define MAX_ARG_STRLEN (6 * 1024 * 1024) #define MAX_ARG_STRINGS 0x7FFFFFFF /* sizeof(linux_binprm->buf) */