Bug 12401

Summary: Problem with argument parsing of "busybox nc" compiled on musl
Product: Busybox Reporter: Rémy Grünblatt <remy>
Component: OtherAssignee: unassigned
Status: NEW ---    
Severity: normal CC: busybox-cvs
Priority: P5    
Version: 1.31.x   
Target Milestone: ---   
Hardware: All   
OS: Linux   
Host: Target:
Build:
Attachments: Busybox .config file

Description Rémy Grünblatt 2019-12-12 10:38:55 UTC
Created attachment 8316 [details]
Busybox .config file

Hello,

I'm using busybox as provided by the Openwrt project, version 1.31.1. The .config is attached.

Busybox is compiled using "musl".

In my installation (openwrt), it seems the option "-e" is not parsed correctly:


    /tmp/rootfs/mnt # busybox nc 192.168.183.1 12345 -e ./foo.sh
    BusyBox v1.31.1 () multi-call binary.
    
    Usage: nc [-iN] [-wN] [-l] [-p PORT] [-f FILE|IPADDR PORT] [-e PROG]
    
    […]

    /tmp/rootfs/mnt # busybox nc -e
    nc: option requires an argument: e
    BusyBox v1.31.1 () multi-call binary.
    
    […]


When busybox is compiled using "glibc", it works (unless POSIXLY_CORRECT is set, then it fails in the same way).

People on the Openwrt IRC channel (that basically investigated this bug) argue that it has to do with the "while (optind < argc)" part: if you put "-e" before the IPADDR PORT, nc considers IPADDR PORT as parameters of the program. If you put -e after the IPADDR PORT, then getopt returns EOF when seeing IPADDR PORT and the code handling the "-e" option isn't hit. They also mention how musl getopt is different from the getopt from glibc (regarding reshuffling argv) and it's related.

Rémy
Comment 1 Uwe Kleine-König 2019-12-13 10:11:29 UTC
Hello,

being one of the guys in the #openwrt channel who debugged the problem I want to make the report a bit clearer by adding some details:

The overall context is (I'm on 1.30.1, but I hope this doesn't matter; a bit simplified):

    while ((opt = getopt(argc, argv, ...) > 0) {
      ...
      if (... opt == 'e' && optind <= argc) {
        ...
        while (optind < argc) {
          ...
          optind++
        }
        ...
      }
      ...
    }
    ...
    argc -= optind;

    if (!argc || argc > 2) bb_show_usage();

This depends on getopt() reordering argv, which musl doesn't do.

On the following commandline:

    busybox nc 127.0.0.1 80 -e /bin/sh

musl's getopt stops processing (i.e. return EOF) on the first argument to nc (i.e. "127.0.0.1") and then bails out because argc is too big.

Doing

    busybox nc -e /bin/sh 127.0.0.1 80

doesn't work either because the inner while loop eats all further parameters. (traditional nc behaves differently here, it only eats exactly one parameter.)

This can be easily reproduced with glibc by setting the env var POSIXLY_CORRECT to 1 (here: Debian with busybox v1.30.1):

    $ POSIXLY_CORRECT=1 busybox nc 127.0.0.1 80 -e /bin/sh
    BusyBox v1.30.1 (Debian 1:1.30.1-4) multi-call binary.
    
    Usage: nc [-iN] [-wN] [-l] [-p PORT] [-f FILE|IPADDR PORT] [-e PROG]
    
    Open a pipe to IP:PORT or FILE
    
    	-l	Listen mode, for inbound connects
    		(use -ll with -e for persistent server)
    	-p PORT	Local port
    	-w SEC	Connect timeout
    	-i SEC	Delay interval for lines sent
    	-f FILE	Use file (ala /dev/ttyS0) instead of network
    	-e PROG	Run PROG after connect
    
    uwe@taurus:~$ busybox nc 127.0.0.1 80 -e /bin/sh
    nc: can't connect to remote host (127.0.0.1): Connection refused

Possible options are:

 - also only use a single argument for -e
 - implement -c which passes its single argument to sh -c and so gives all flexibility that -e doesn't have with only a single argument.

Best regards
Uwe