From nobody Wed Nov 3 13:35:40 2021 X-Original-To: freebsd-hackers@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 3B24A183EB8B for ; Wed, 3 Nov 2021 13:36:02 +0000 (UTC) (envelope-from kostikbel@gmail.com) Received: from kib.kiev.ua (kib.kiev.ua [IPv6:2001:470:d5e7:1::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4Hknpn6ZHyz4lnk for ; Wed, 3 Nov 2021 13:36:01 +0000 (UTC) (envelope-from kostikbel@gmail.com) Received: from tom.home (kib@localhost [127.0.0.1]) by kib.kiev.ua (8.16.1/8.16.1) with ESMTPS id 1A3DZgbV019223 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NO); Wed, 3 Nov 2021 15:35:46 +0200 (EET) (envelope-from kostikbel@gmail.com) DKIM-Filter: OpenDKIM Filter v2.10.3 kib.kiev.ua 1A3DZgbV019223 Received: (from kostik@localhost) by tom.home (8.16.1/8.16.1/Submit) id 1A3DZeif019222; Wed, 3 Nov 2021 15:35:40 +0200 (EET) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: tom.home: kostik set sender to kostikbel@gmail.com using -f Date: Wed, 3 Nov 2021 15:35:40 +0200 From: Konstantin Belousov To: Drew DeVault Cc: freebsd-hackers@freebsd.org Subject: Re: Complicated interactions between O_EXEC, fdescfs, fexecve, and shebangs Message-ID: References: List-Id: Technical discussions relating to FreeBSD List-Archive: https://lists.freebsd.org/archives/freebsd-hackers List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-freebsd-hackers@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,BAYES_00, DKIM_ADSP_CUSTOM_MED,FORGED_GMAIL_RCVD,FREEMAIL_FROM, NML_ADSP_CUSTOM_MED autolearn=no autolearn_force=no version=3.4.5 X-Spam-Checker-Version: SpamAssassin 3.4.5 (2021-03-20) on tom.home X-Rspamd-Queue-Id: 4Hknpn6ZHyz4lnk X-Spamd-Bar: ---- Authentication-Results: mx1.freebsd.org; none X-Spamd-Result: default: False [-4.00 / 15.00]; REPLY(-4.00)[] X-ThisMailContainsUnwantedMimeParts: N On Wed, Nov 03, 2021 at 12:30:26PM +0100, Drew DeVault wrote: > Note: I am not subscribed to this list, please use reply-all to keep me > on the thread. Thanks! > > $ uname -a > FreeBSD megumin 13.0-RELEASE FreeBSD 13.0-RELEASE #0 releng/13.0-n244733-ea31abc261f: Fri Apr 9 04:24:09 UTC 2021 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 > > This problem starts with the following program: > > #include > #include > > extern char **environ; > > int main(void) { > int fd = open("./test.sh", O_EXEC); > char *argv[] = { > "./test.sh", > NULL > }; > fexecve(fd, argv, environ); > } > > Given this test.sh, which is executable: > > #!/bin/sh > echo hello world > > This program produces the following error: > > /bin/sh: cannot open /dev/fd/3: Permission denied > > The program works fine with O_RDONLY instead, which makes some sense. > The way this works is that the kernel rewrites argv to {"/bin/sh", > "/dev/fd/%d"}, where %d is the file descriptor passed to fexecat. The > interpreter then has to open this file for reading, so it needs the read > bit set. fdescfs preserves the permissions of the file descriptor which > was originally opened, so the read bit is missing with O_EXEC. Q.E.D. > > The fix is to set O_RDONLY and mount fdescfs. If nothing else comes of > this, I would like to request that FreeBSD consider mounting fdescfs by > default, so that fexecve can be reliably expected to work correctly with > interpreters. Otherwise, the value proposition of fexecve is severely > limited. > > However, a few other problems came up while looking into this. > > The investigation was made more difficult by the fact that open(2) is > documented in the man page as producing EINVAL when O_EXEC is combined > with O_RDONLY, but this is not so: no error occurs. This is because > O_RDONLY is, in fact, not a bit: it is zero. You cannot NOT provide > O_RDONLY to an open call. RhodiumToad on #freebsd IRC gave a possible > improvement for the man page: > > > Only one of O_EXEC, O_RDWR and O_WRONLY may be specified. > > The other issue is that this essentially makes O_EXEC useless outside of > some specific cases, where the user knows for certain that the file > being executed is not a script. The combination of O_EXEC and fexecve > cannot generalize to support all use-cases of execve, which is > frustrating because my code either (A) cannot be TOCTOU or (B) needs > some awful special cases. Even in case (B), it would not generalize to > the case where I have execute, but not read, permission for a script, > but the interpreter has both. > > I'm not sure what the answer for any of this is. > > By way of contrast, Linux solves this problem a bit differently. It does > not have O_EXEC, but it does have O_PATH, which opens a file descriptor > without read, write, OR execute, but simply to keep track of an inode > reference. fexecve on Linux then uses a similar /dev/fd trick, but the > file in /dev/fd has no mode bits set and I'm not sure why it works. FreeBSD also has O_PATH. There are two differences with Linux: - FreeBSD requires O_EXEC to be specified together with O_PATH, if intent is to use the resulting file descriptor with fexecve(2). In fact this can be removed, see https://reviews.freebsd.org/D32821 - Semantic of the FreeBSD fdescfs open(2) is different, to get the behavior similar to Linux, you need to specify "nodup" mount option, see fdescfs(5).