dash.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jilles Tjoelker <jilles@stack.nl>
To: earnestly <zibeon@gmail.com>, dash@vger.kernel.org
Subject: Re: getopts appears to not be shifting $@ when consuming options
Date: Fri, 29 Jan 2021 21:36:50 +0100	[thread overview]
Message-ID: <20210129203650.GA21262@stack.nl> (raw)
In-Reply-To: <YBRTFXGM6pLwR5hg@teapot>

On Fri, Jan 29, 2021 at 06:25:25PM +0000, earnestly wrote:
> In this example dash will repeatedly append 'attr=foo' to the list of
> parameters in an infinite loop:

>     #!/bin/dash -x

>     while getopts :a: arg -a foo -a bar; do
>         case $arg in
>             a) set -- "$@" attr="$OPTARG"
>         esac
>     done
>     shift "$((OPTIND - 1))"

> Instead I expected this to result in parameter list containing
> 'attr=foo' and 'attr=bar'.

Hmm, that's pretty clever. By passing the parameters to be parsed to the
getopts command instead of via $@, it is possible to modify $@ without
violating POSIX's rule that only allows parsing a single unmodified set
of parameters (unless getopts is reset via OPTIND=1).

Unfortunately, dash and FreeBSD sh reset the getopts state when the
positional parameters are modified via set or shift. They probably do
this to avoid use after free and out of bounds memory access when a
script violates POSIX's rule. However, when the parameters come from the
getopts command, there is no violation of the rule and no memory
unsafety problem.

> This works in all shells I have been able to test with the exception of
> busybox sh:

>     * sh   (bash)
>     * bash (All versions from 1.14 through 5.1.4)
>     * mksh (MIRBSD KSH R59 2020/05/16)
>     * ksh  (93u+)
>     * zsh  (5.8)
>     * zsh --emulate sh
>     * heirloom-sh (bourne)

> The only workaround I've found is to explicitly use `shift 2` in the a)
> case and obviate the final shift using OPTIND.  This will unfortunately
> break every other shell.

Since you need code to "un-eval" a list of parameters to construct the
getopts command above in a general case, a better workaround would be to
use that code to construct the attr="$OPTARG" list.

If your actual code is more like:

set -- -a foo -a bar # for testing
while getopts :a: arg; do
...

then the script violates the rule I mentioned, and has unspecified
results.

-- 
Jilles Tjoelker

  parent reply	other threads:[~2021-01-29 20:38 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-29 18:25 getopts appears to not be shifting $@ when consuming options earnestly
2021-01-29 20:15 ` Harald van Dijk
2021-01-30 15:31   ` Harald van Dijk
2021-01-29 20:36 ` Jilles Tjoelker [this message]
2021-01-29 21:19   ` Harald van Dijk
2021-01-29 22:25   ` earnestly
2021-01-30  6:39     ` Vladimir N. Oleynik
2021-01-30  7:36     ` Vladimir N. Oleynik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210129203650.GA21262@stack.nl \
    --to=jilles@stack.nl \
    --cc=dash@vger.kernel.org \
    --cc=zibeon@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).