Skip to content

Commit

Permalink
iamroot, interpreter-script: run empty executable files
Browse files Browse the repository at this point in the history
TL;DR; This runs empty executable files via shell interpreter.

The package scripts devuan-baseconf.postinst is empty on Devuan jessie;
this leads to the error below:

	Setting up devuan-baseconf (0.6.4+devuan1.3) ...
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Input/output error
	dpkg: error processing package devuan-baseconf (--configure):
	subprocess installed post-installation script returned error exit status 2

See:

	gportay@archlinux ~/src/iamroot $ ls -l amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.*
	-rw-r--r-- 1 gportay gportay  76 May 24  2017 amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.conffiles
	-rw-r--r-- 1 gportay gportay 307 May 10 09:16 amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.list
	-rw-r--r-- 1 gportay gportay 151 May 24  2017 amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.md5sums
	-rwxr-xr-x 1 gportay gportay   0 May 24  2017 amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.postinst
	-rwxr-xr-x 1 gportay gportay   0 May 24  2017 amd64-devuan-jessie-rootfs/var/lib/dpkg/info/devuan-baseconf.postrm

See also[1][2].

According to the Open Group Base Specifications Issue 7, 2018 edition,
Chapter 2.9.1, Point Command Search and Execution 1.e.i.b. and 2.[3]:

	If the execl() function fails due to an error equivalent to the
	[ENOEXEC] error defined in the System Interfaces volume of
	POSIX.1-2017, the shell shall execute a command equivalent to
	having a shell invoked with the pathname resulting from the
	search as its first operand, with any remaining arguments passed
	to the new shell, except that the value of "$0" in the new shell
	may be set to the command name. If the executable file is not a
	text file, the shell may bypass this command execution. In this
	case, it shall write an error message, and shall return an exit
	status of 126.

	(...)

	If the execl() function fails due to an error equivalent to the
	[ENOEXEC] error, the shell shall execute a command equivalent to
	having a shell invoked with the command name as its first
	operand, with any remaining arguments passed to the new shell.
	If the executable file is not a text file, the shell may bypass
	this command execution. In this case, it shall write an error
	message and shall return an exit status of 126.

According to 6) in execute_cmd.c of bash(1)[4][5]:

	 1) fork ()
	 2) connect pipes
	 3) look up the command
	 4) do redirections
	 5) execve ()
	 6) If the execve failed, see if the file has executable mode
	    set. If so, and it isn't a directory, then execute its
	    contents as a shell script.

According to shell/ash.c of busybox(1)[6]:

	/* Run "cmd" as a shell script:
	 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
	 * "If the execve() function fails with ENOEXEC, the shell
	 * shall execute a command equivalent to having a shell invoked
	 * with the command name as its first operand,
	 * with any remaining arguments passed to the new shell"
	 *
	 * That is, do not use $SHELL, user's shell, or /bin/sh;
	 * just call ourselves.
	 *
	 * Note that bash reads ~80 chars of the file, and if it sees
	 * a zero byte before it sees newline, it doesn't try to
	 * interpret it, but fails with "cannot execute binary file"
	 * message and exit code 126. For one, this prevents attempts
	 * to interpret foreign ELF binaries as shell scripts.
	 */

The functions execve(), posix_spawn() and co. bypass the kernel by
rewritting the cmdline using the dynamic loader to run the program.

This hacks the function __can_exec() to return 1 if empty-file, and the
function __interpreter_script_hashbang() to return _PATH_BSHELL to cause
to "execve($ROOT/bin/sh, {argv[0], program, ...}, __environ)".

Note: This should be implemented correctly.

Fixes:

	gportay@archlinux ~/src/iamroot $ make amd64-devuan-jessie-rootfs
	ido --multiarch --preserve-env=PERL_DL_NONLAZY --preserve-env=LDCONFIG_NOTRIGGER mkdir -p amd64-devuan-jessie-rootfs
	ido --multiarch --preserve-env=PERL_DL_NONLAZY --preserve-env=LDCONFIG_NOTRIGGER debootstrap --keep-debootstrap-dir --arch=amd64  jessie amd64-devuan-jessie-rootfs http://archive.devuan.org/merged/ support/ceres
	I: Target architecture can be executed
	I: Retrieving InRelease
	I: Checking Release signature
	I: Valid Release signature (key id 72E3CB773315DFA2E464743D94532124541922FB)
	I: Retrieving Packages
	I: Validating Packages
	I: Resolving dependencies of required packages...
	I: Resolving dependencies of base packages...
	I: Checking component main on http://archive.devuan.org/merged...
	(...)
	W: Failure while configuring base packages.  This will be re-attempted up to five times.
	W: See /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
	I: Configuring devuan-baseconf...
	W: Failure while configuring base packages.  This will be re-attempted up to five times.
	W: See /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
	W: Failure while configuring base packages.  This will be re-attempted up to five times.
	W: See /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
	W: Failure while configuring base packages.  This will be re-attempted up to five times.
	W: See /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
	W: Failure while configuring base packages.  This will be re-attempted up to five times.
	W: See /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)

	gportay@archlinux ~/src/iamroot $ cat /home/gportay/src/iamroot/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log
	(...)
	Setting up devuan-baseconf (0.6.4+devuan1.3) ...
	__fcan_exec: Input/output error
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
	dpkg: error processing package devuan-baseconf (--configure):
	subprocess installed post-installation script returned error exit status 2
	(...)
	Errors were encountered while processing:
	 devuan-baseconf
	Setting up devuan-baseconf ...
	__fcan_exec: Input/output error
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
	dpkg: error processing package devuan-baseconf (--configure):
	 subprocess installed post-installation script returned error exit status 2
	Errors were encountered while processing:
	 devuan-baseconf
	Setting up devuan-baseconf ...
	__fcan_exec: Input/output error
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
	dpkg: error processing package devuan-baseconf (--configure):
	 subprocess installed post-installation script returned error exit status 2
	Errors were encountered while processing:
	 devuan-baseconf
	Setting up devuan-baseconf ...
	__fcan_exec: Input/output error
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
	dpkg: error processing package devuan-baseconf (--configure):
	 subprocess installed post-installation script returned error exit status 2
	Errors were encountered while processing:
	 devuan-baseconf
	Setting up devuan-baseconf ...
	__fcan_exec: Input/output error
	dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
	dpkg: error processing package devuan-baseconf (--configure):
	 subprocess installed post-installation script returned error exit status 2
	Errors were encountered while processing:
	 devuan-baseconf

[1]: https://utcc.utoronto.ca/~cks/space/blog/unix/BourneCommentHistory
[2]: https://utcc.utoronto.ca/~cks/space/blog/unix/EmptyFileWhyTrue
[3]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
[4]: http://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c?h=bash-5.2#n5569
[5]: http://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c?h=bash-5.2#n5992
[6]: https://git.busybox.net/busybox/tree/shell/ash.c#n8282
  • Loading branch information
gportay committed May 13, 2024
1 parent 5d193c9 commit e85244f
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 46 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
directory
- Create the `64` weak aliases `__futimesat64()`, `__lutimes64()`,
`__utime64()` and `__utimes64()`
- Run empty executable files via the bourne shell interpreter

### Changed

Expand Down
7 changes: 6 additions & 1 deletion iamroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,17 @@ static int __fcan_exec(int fd)
char magic[4];
ssize_t siz;

/* Read magic */
siz = read(fd, magic, sizeof(magic));
if (siz == -1)
return -1;
else if ((size_t)siz < sizeof(magic))
else if (siz != 0 && (size_t)siz < sizeof(magic))
return __set_errno_and_perror(EIO, -1);

/* It is an empty file */
if (siz == 0)
return 1;

/* It is an interpreter-script */
if (memcmp(magic, SCRIPTMAG, SSCRIPTMAG) == 0)
return 1;
Expand Down
51 changes: 51 additions & 0 deletions interpreter-script.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <paths.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "iamroot.h"

Expand All @@ -18,6 +20,7 @@ extern int next_open(const char *, int, mode_t);
hidden ssize_t __interpreter_script_hashbang(const char *path, char *buf,
size_t bufsiz, off_t offset)
{
struct stat statbuf;
ssize_t ret;
char *d, *s;
int fd;
Expand Down Expand Up @@ -59,6 +62,54 @@ hidden ssize_t __interpreter_script_hashbang(const char *path, char *buf,
if (fd == -1)
return -1;

ret = fstat(fd, &statbuf);
if (ret == -1)
goto close;

/*
* According to execute_cmd.c in bash(1):
*
* 1) fork ()
* 2) connect pipes
* 3) look up the command
* 4) do redirections
* 5) execve ()
* 6) If the execve failed, see if the file has executable mode set.
* If so, and it isn't a directory, then execute its contents as
* a shell script.
*
* According to shell/ash.c in busybox(1):
*
* Run "cmd" as a shell script:
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
* "If the execve() function fails with ENOEXEC, the shell
* shall execute a command equivalent to having a shell invoked
* with the command name as its first operand,
* with any remaining arguments passed to the new shell"
*
* That is, do not use $SHELL, user's shell, or /bin/sh;
* just call ourselves.
*
* Note that bash reads ~80 chars of the file, and if it sees
* a zero byte before it sees newline, it doesn't try to
* interpret it, but fails with "cannot execute binary file"
* message and exit code 126. For one, this prevents attempts
* to interpret foreign ELF binaries as shell scripts.
*
* TODO: Implement this hack correctly.
*/
/* File is empty */
if (statbuf.st_size == 0) {
int n;

n = _snprintf(&buf[offset], bufsiz, "%s", _PATH_BSHELL);
if (n == -1)
goto close;

ret = n;
goto close;
}

ret = read(fd, &buf[offset], bufsiz-offset-1); /* NULL-terminated */
if (ret == -1)
goto close;
Expand Down
47 changes: 2 additions & 45 deletions support/amd64-devuan-jessie-rootfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ I: Configuring libnettle4...
I: Configuring debian-archive-keyring...
I: Configuring vim-common...
I: Configuring devuan-baseconf...
I: Configuring libnfnetlink0...
I: Configuring libstdc++6...
I: Configuring libffi6...
I: Configuring libnewt0.52...
Expand Down Expand Up @@ -716,17 +717,7 @@ I: Configuring isc-dhcp-common...
I: Configuring isc-dhcp-client...
I: Configuring tasksel...
I: Configuring tasksel-data...
W: Failure while configuring base packages. This will be re-attempted up to five times.
W: See $ROOT/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
I: Configuring devuan-baseconf...
W: Failure while configuring base packages. This will be re-attempted up to five times.
W: See $ROOT/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
W: Failure while configuring base packages. This will be re-attempted up to five times.
W: See $ROOT/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
W: Failure while configuring base packages. This will be re-attempted up to five times.
W: See $ROOT/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
W: Failure while configuring base packages. This will be re-attempted up to five times.
W: See $ROOT/amd64-devuan-jessie-rootfs/debootstrap/debootstrap.log for details (possibly the package devuan-baseconf is at fault)
I: Base system installed successfully.
gpgv: Signature made Day Mon dd hh:mm:ss YYYY TZ
gpgv: using RSA key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
gpgv: Good signature from "Devuan Repository (Primary Devuan signing key) <repository@devuan.org>"
Expand Down Expand Up @@ -1698,10 +1689,6 @@ Setting up libnettle4 ...
Setting up debian-archive-keyring ...
Setting up vim-common ...
Setting up devuan-baseconf ...
__fcan_exec: Input/output error
dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
dpkg: error processing package devuan-baseconf (--configure):
subprocess installed post-installation script returned error exit status 2
Setting up libnfnetlink0 ...
Setting up libstdc++6 ...
Setting up libffi6 ...
Expand Down Expand Up @@ -1777,33 +1764,3 @@ Setting up isc-dhcp-common ...
Setting up isc-dhcp-client ...
Setting up tasksel ...
Setting up tasksel-data ...
Errors were encountered while processing:
devuan-baseconf
Setting up devuan-baseconf ...
__fcan_exec: Input/output error
dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
dpkg: error processing package devuan-baseconf (--configure):
subprocess installed post-installation script returned error exit status 2
Errors were encountered while processing:
devuan-baseconf
Setting up devuan-baseconf ...
__fcan_exec: Input/output error
dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
dpkg: error processing package devuan-baseconf (--configure):
subprocess installed post-installation script returned error exit status 2
Errors were encountered while processing:
devuan-baseconf
Setting up devuan-baseconf ...
__fcan_exec: Input/output error
dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
dpkg: error processing package devuan-baseconf (--configure):
subprocess installed post-installation script returned error exit status 2
Errors were encountered while processing:
devuan-baseconf
Setting up devuan-baseconf ...
__fcan_exec: Input/output error
dpkg (subprocess): unable to execute installed post-installation script (/var/lib/dpkg/info/devuan-baseconf.postinst): Invalid argument
dpkg: error processing package devuan-baseconf (--configure):
subprocess installed post-installation script returned error exit status 2
Errors were encountered while processing:
devuan-baseconf

0 comments on commit e85244f

Please sign in to comment.