[Toybox] Shell corner cases.

Rob Landley rob at landley.net
Thu May 1 12:44:36 PDT 2025


On 5/1/25 10:29, Chet Ramey wrote:
> On 4/30/25 6:23 PM, Rob Landley wrote:
> 
>> Somebody forwarded that to me from an exploit how-to site. It's 
>> apparently a trick used to subvert scripts that call things via 
>> absolute path to avoid malicious $PATH.
> 
> I suppose if you can get a script to source an arbitrary file, you can do
> pretty much anything.

$ command /usr/bin/whoami
landley
$ command() { echo nope; }
$ command /usr/bin/whoami
nope

What was the _benefit_ of allowing / in command names?

>> $ x() { echo hello; }; D=x; $D
>> hello
> 
> OK? Commands are parsed, then executed.

To me, that seemed like analogous behavior working on the same line.

Mostly the use of alias I've encountered seems to be things like alias 
ls='ls -l' allowing additional arguments, although ls() { ls -l "$@"; } 
seems like it could do it too. The _unique_ feature is stuff like:

   $ alias ls='ls >'
   $ ls
   bash: syntax error near unexpected token `newline'

Which I've never seen the utility of, other than people who do variants 
of the old "define begin {" stuff pascal programmers used to do when 
forced to use C...

>>> Since alias expansion happens when the command is
>>> read and tokenized,
>>
>> Yeah, that was the weird part. My code breaks argv[] into separate 
>> chunks but doesn't try to figure out what they _mean_ until it needs 
>> to run them, because:
>>
>> $ alias one='$X'
>> $ X='$A'; A=$X one
>> bash: $A: command not found
>>
>> Is still figuring out that it has to move past the prefix assignment 
>> to expand the alias, so you're saying your "tokenization" recognizes 
>> prefix assignments...
> 
> Alias expansion happens on the first word of a simple command (for our
> purposes here). The first word of a simple command that's going to be
> eligible for alias expansion is a WORD, not an ASSIGNMENT_WORD.
>
> The parser knows when it's going to read a command, so it knows when to
> expect a simple command, and if it reads an assignment statement in that
> position, it knows it's an ASSIGNMENT_WORD. Only WORDs in the right
> position are eligible for alias expansion -- it's context-dependent.

It works for prefix assignments but not:

$ alias blah='echo hello'
$ >file blah
$ cat blah
cat: blah: No such file or directory

(Don't ask me why the error message went to stdout instead of stderr, 
but the $ prompt goes to stderr. I think I need to walk away from the 
keyboard for a bit...)

> As POSIX puts it:
> 
> "Further distinction on TOKEN is context-dependent. It may be that the same
> TOKEN yields WORD, a NAME, an ASSIGNMENT_WORD, or one of the reserved words
> below, dependent upon the context."
> 
>>
>>> the list will be parsed before any of the commands
>>> are executed. This is how Bourne-style shells work.
>>
>> The logic of what counts as a "line" in this context is unclear to me:
> 
> Oh, we had a rocking discussion about that topic, and the comments here
> aren't half of it.
> 
> https://www.austingroupbugs.net/view.php?id=953

Oh joy.

>> $ bash -c $'echo $LINENO;alias a=b\necho $LINENO;a'
>> 1
>> 2
>> bash: line 2: a: command not found
> 
> Anywhere the shell executes a string (the command_string argument to
> `sh -c', `eval', command substitution bodies), it parses the entire string
> as a compound_list and then executes that list.

I just did fmemopen(buf, strlen(buf), "r") and fed it to the same line 
reading loop as everything else. You're saying I should write extra code 
to behave differently for the special case.

>> Wouldn't trigger (like it does for functions).
>>
>> Then again busybox ash, mksh, zsh, and even dash are all doing this 
>> too, so I guess "compatible" beats "sane". Maybe it's so you can do:
>>
>> alias potato=fruitbat
>> source filename
>> unalias potato
> 
> See above about compound_list parsing.

Sigh.

>> And, of course:
>>
>> $ alias one=two
>> $ on\e
>> bash: one: command not found
> 
> When that is tokenized, the backslash is still part of the word, so it
> can't match an alias name.

Only because:

$ alias on\\e=two
bash: alias: `on\e': invalid alias name

Which function definition _doesn't_ do...

My first pass at implementing this just recycled the "is this a valid 
variable name" logic:

$ abc/def=ghi
bash: abc/def=ghi: No such file or directory

But functions can have / in them for reasons.

Possibly I should just get this wrong and wait to see who complains...

> Chet

Rob


More information about the Toybox mailing list