From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=tamu.edu Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=tamu.edu header.i=@tamu.edu header.a=rsa-sha256 header.s=ppae6d7b header.b=Jc5jC/Gj; dkim-atps=neutral Received: from mx0b-00178102.pphosted.com (mx0b-00178102.pphosted.com [148.163.139.245]) by passt.top (Postfix) with ESMTPS id 0D1A75A0262 for ; Fri, 31 Jul 2026 14:18:26 +0200 (CEST) Received: from pps.filterd (m0169869.ppops.net [127.0.0.1]) by mx0b-00178102.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66VBavKT463100 for ; Fri, 31 Jul 2026 07:18:25 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tamu.edu; h= content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to; s=ppae6d7b; bh=8AaQGWaMft2PNZxD5Jc+J4qFR sNRnKCd++tB5K+D8i0=; b=Jc5jC/Gji4DuAx0IXTv5dB5y/oryltwZG+bl8MfLg tuCRRtGnKeZfjEDY1+a8Lyge10jg/wBpywHnan2Qkm/EguOgMF6NTz7TO1Ezv5eJ ZhQPaHE75glsk+Rkfhih58Chjw+bAHT51cGgvUKvdCGfl3AgMP1nUES5frw0ChYt 4Lu1EchJHQcxAFFKjbNNCVeOx7pf2UvV4S5UoJycVNGN4KCudNoV0hanwP7L+faU UOkllLNm8rk6oQ8SHPrTE0IN48MNbta2qoaN6SR4PdLWRX5wZPCpg3cbL5n/8WSn NyT95fLO3sGfNLg/wfe71mrG9JDB+4h9aO0fmv4hzU3OQ== Received: from ph7pr06cu001.outbound.protection.outlook.com (mail-westus3azon11010059.outbound.protection.outlook.com [52.101.201.59]) by mx0b-00178102.pphosted.com (PPS) with ESMTPS id 4fr6hq69ch-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT) for ; Fri, 31 Jul 2026 07:18:24 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=em5LO2OnjoV9/9+D4un6d224gOAoiYHjPfLqfVNxbibKHcPZnfACgK2BiZWefgxI5nl4ijVbAAFqeSruWvZOFqg+E+JCRRDSn9k4lMizMsJJLQZYcGU0MTVfXbDFOwPoKfQ6R1IpxJpXUec4A/17QM3GAt/wxbVAhwso3leDVg8bBW7OPOvyWbSGCwZZGvbzWc60q9TxR5pyzveG/j8f49qb4ZmKWDi7RZMYcRD02ET/fvDvjUjM6RJBdB8J13X+rfflMa4OlsqBXtNPe4kyXYCdGe5od5lJtlK1WWlbn72GPPGxBN2hVYzEWCFBKRVxVrDngHcr0FYMHhlEIhDCHA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=8AaQGWaMft2PNZxD5Jc+J4qFRsNRnKCd++tB5K+D8i0=; b=GXHAm7GNzHvYqVCIihlY/K85MvLfjoiJZjTtDY6hdhbiLD19pkEdefmL14RTiiNpif4yrFzJbqpcTCfcOY/JjEFC2F4SyoNmHu/vXwqNmB6Xr/fqszWEmJ6IXZ3oHs+sFparpJ7eXZTuVfKnBPGWfq7z2f2JnWVa3Y1lynS+blTh+JBdoGWABQ/o8MHzBBMKoVPw6+77ugPacq85B9SjwMiSAwUq6ySBzMT8eqr7lHlLLLzrY6qjHDEEgiuw7CoFxR8JABY5098ve/P96jeryfiJje/rtbZUZEojSE29QiTKTvUPuTeVELdb2y4qx0TO6HDRmwSy/4rgmMwLAxD0EQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=tamu.edu; dmarc=pass action=none header.from=tamu.edu; dkim=pass header.d=tamu.edu; arc=none Received: from DM3PR11MB8713.namprd11.prod.outlook.com (2603:10b6:0:45::15) by CH9PR11MB9904.namprd11.prod.outlook.com (2603:10b6:610:343::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.270.16; Fri, 31 Jul 2026 12:18:21 +0000 Received: from DM3PR11MB8713.namprd11.prod.outlook.com ([fe80::e63e:f56e:ed80:ee28]) by DM3PR11MB8713.namprd11.prod.outlook.com ([fe80::e63e:f56e:ed80:ee28%4]) with mapi id 15.21.0270.015; Fri, 31 Jul 2026 12:18:21 +0000 From: "Lawrence, Richard E" To: "passt-dev@passt.top" Subject: Re: [PATCH] feat: Pass open files to child in pasta mode Thread-Topic: [PATCH] feat: Pass open files to child in pasta mode Thread-Index: AQHdILKv26K7D652NUiXcGq1DrH/oLaHi2M1 Date: Fri, 31 Jul 2026 12:18:20 +0000 Message-ID: References: <20260731060341.302560-1-rlawrence@tamu.edu> In-Reply-To: <20260731060341.302560-1-rlawrence@tamu.edu> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: msip_labels: x-ms-publictraffictype: Email x-ms-traffictypediagnostic: DM3PR11MB8713:EE_|CH9PR11MB9904:EE_ x-ms-office365-filtering-correlation-id: 09024d9c-3a09-4857-1cd9-08deeefdcf8f x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0;ARA:13230040|366016|1800799024|786006|23010399003|376014|22082099003|18002099003|56012099006|11063799006|8096899003|38070700021|6133799003|10067099003; x-microsoft-antispam-message-info: j9iHKS2d8JmqsmvaL2HwYTyLUZ4anuhAyZuFC2OfmAFdGYWUHcBHKwJjhJIgWdPe5CPFyDoD+IvN5uvumEU1CCyAo5xRb/HTZ5qenZGtL/wbE3P+tDV4XQtnYjB33PfesSbF0/Mkq2hcHXtMyNzwZoJ8nk3NHAzjmkwooiKdf4NcMWDStyUXRpP3CA+EBYkBKSrhJhGA7AoLGL2USaOvgYDrdZJxi2GH0ass5/JgjsqhMY27yPRKsK2qJnQr5dGeAAV+K2eY6G9ofwv7gur1TpWgqLHXqoEOn9VexZ6uTzgL2Gkkxcipvpk6pXkwkW7e/MBmZRseVvL9byGX+lH+IRxR2BG3NvZbdJAohJl5JHt9/DCx6RTjNUaTK9uBEXMwdP9qQoAP0//dANuoJ1hpyPLTQnmZQVdsSL/w39NuTdlljpZdpek6n7J/ZCcAAbd4cM2c+CCvpquZwHhQHDr5I10gtkSfqJDpLbkhOglcaifcuOERedjENp6jv8KVcCAzdd6KQGMPoCrVPvl9fwx65Bba83bJ+JryoAfTl3Dku5z4DDvDG1JTCgxNmqrw79Stj5niixFOK+b53Hy0SUsKRohIcE9YYUT1ezhawYJyjQQuEGkk2QcSPu4e4YFXgkB8bP9+zajSCAQ/fZRtXHV8HvlHpdgTpUCY2gU5crvMvmZWgJU6rVGXNUvAsWE/aTq9bfcjT6IT7/MmTu5SC6hgxBSN24pz7QvK6ThSGn34+/0= x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DM3PR11MB8713.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(1800799024)(786006)(23010399003)(376014)(22082099003)(18002099003)(56012099006)(11063799006)(8096899003)(38070700021)(6133799003)(10067099003);DIR:OUT;SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?d7ELX3gxXwRKyXMCBsoM5Wb2CrR8bjHLqUc2QD12AXXKZrTROrMcOMoYM5xY?= =?us-ascii?Q?Zqj9UXpOqYoNFTi2g5AZ0KftHEzeby+IuwQXIvoP3A8BN+XDqBlZMNxlq6Ye?= =?us-ascii?Q?mHk1PCg8Zlk6xLmDhdG6vKzq96veXaXjc0nZjd/LCHnqAjZKYL++qqlfOPAY?= =?us-ascii?Q?RaXl1nBxVQXkdcorjaecRIGiJ6FP6hQkzeHhpMRgXUPkWOA7bZBkrM8lRm3e?= =?us-ascii?Q?X30a/RUEmYegLiJvWNHC9O3wybsOgEy8iMF5OkUc7Luo5AIc+XBrSwyoxPNk?= =?us-ascii?Q?Pw2GsOPUpTRtc0Ytl9YG39FeKb9DN8Ln8lwSimOlUlA/ohDU30oYdm2y0mc9?= =?us-ascii?Q?aN40wZeueA7m7hT5U0T0exUf4AIKUgUb3h0AXxaEIy+ZDw3DVxIDbF0rYokE?= =?us-ascii?Q?6DlGEeUldfbexFLY3h7IQRLNDLiVNDYV5vxy311GsNK/BCzPosF9WHQmfIak?= =?us-ascii?Q?qMohUeBqwI8TQ8z1lXPDHAW49S7vxNwalPNBSwR0G4Ex7zo+FR/P74z52YTV?= =?us-ascii?Q?Kvf+bagCW9ve0zmYgtAAZlVZbGVyvEhNCoSnJyKsiqvdJMdiRLMBK/MLS3Ey?= =?us-ascii?Q?CkpSsDiB+aswsg+rfhk887KrQaCH6V6jDnOLx3uXQOZbX5vCdCcq+cGSLO5P?= =?us-ascii?Q?mRrudscgKDCPuyA3gFMwxvVIRHqj1ZJR4PbZ0lUGnoNL7dabe8BVYfcwu0b4?= =?us-ascii?Q?1Iu/AqNCKVBU/uMPrfTL5QsQ2cHuj1ztER0rr3mjWybA+Sc3OHtv5XYg6KzV?= =?us-ascii?Q?QbvkwMJMragIWqpuvSt9211lpj7xOQKsF4DdrpYrktkNXuatI11AeE0yvPoI?= =?us-ascii?Q?Di3Qh8ouIwnUyLgHHoLhwGsRChgmPnYBQuk63oORnKcCZ5bKpRZN7M40o7TT?= =?us-ascii?Q?A10nPKiDscvehbHIIIZwpkjlRh1ya66PtznMBo5djcDKnBpGK4eD70p/BtWm?= =?us-ascii?Q?gNBanl8Rj4vXaQCphxxrg+PUd9lkH2VNXIBpbnCGA2flGGM01qfTIhq4oUtf?= =?us-ascii?Q?ONHlODcTJ75ZFrnSxmFdYxJpCRm6pLdSRunJggpTK1z9nEosXTbFg2HZHSUp?= =?us-ascii?Q?uzSpeWToI9e7ayv80yGW32Xshgs+JlmWs1Hz5OfNiLqw7ZwZe0fj97H+50VY?= =?us-ascii?Q?NTrEy4S42qBWXyKGErXdo3vj7769Myvypg7XtVRnrER5L6Qegby4uVn2b/f7?= =?us-ascii?Q?Tv/0PlraR0Ki1Z4/G0ynziFPa6GcoLgOEqmJGwcWp2E/zXNX/PJm7SZUAKnl?= =?us-ascii?Q?9M7pmiVXa6CwvB78NuoQpS3Kzs5yiEDuDQTJsnoj5Cc1i9gNF9UcozVMucB8?= =?us-ascii?Q?FBTWHnrJTZCjCUtL8G7NEgpia0zgAcGdS95mCPcnaaqyQV5benOgHAFkQwMV?= =?us-ascii?Q?cOiKWDtibSjo/MKLWZLtboa7ajZKvOXQQaOCMGhz4SpCudi6Ulz7W5qaR+EY?= =?us-ascii?Q?If3w6Kmy7eIJPb6mSAB8o93dqsyIEMM17H9+tTJp+J8OrhsLJ+HlgJRlqdUo?= =?us-ascii?Q?cD+BpfRJARzPTGIp8KQz1Yf+R8dzCJQTSWdtGw9+8oLSkYQziRp5IMKkcEer?= =?us-ascii?Q?PlDyNxVgTL7j/bJ9uXGaNKevv6yLKwCrPAfEIEvBZrHU7C3/A5Z0Grr8TF5w?= =?us-ascii?Q?7mfnDeoo0oxCwryv2VSe9dX6kc9ma+uSZ2RVxUQiNfMot1fmXTCH38GD6SvS?= =?us-ascii?Q?3x6tFWcHdS5ia0yDPXKnig/jeLUI2x7t7hVJFff4vr0spZHr?= Content-Type: multipart/alternative; boundary="_000_DM3PR11MB871373A3A2D6F1F07916FE5DDAC82DM3PR11MB8713namp_" MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: lWvcJQzViso3X6NEzKbSLhzJiRfpwwC5NPEnkMYwhhssEyNhycM7cYl6qNsRUd+8eeyttuC+HoKjNlE3JHItcUKRXF6acyYOGEEfP20BCa12Zcw1/S83i8qiGLh4oCqcOT32VLSm5mFLwsOJ7/OTZIxOJ1fTHNdtNIQsDqWoJan5nG87YmuPYwrUZ+AHeKd4YhjiYOYbZ654WSQi0wBElc2xvsmVydymFP7AHXJ0BHOkFbTGdATfOyq0LbmQQy5VVCUZZWEStGHvFMic3gGe9aUl/jtxgFQ6Tre3UUSPxYfQSKX59EI8nXgVeOj99LvHkz7c1Nuvbl0KMHgrC2XCyA== X-OriginatorOrg: tamu.edu X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: DM3PR11MB8713.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 09024d9c-3a09-4857-1cd9-08deeefdcf8f X-MS-Exchange-CrossTenant-originalarrivaltime: 31 Jul 2026 12:18:20.9317 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 68f381e3-46da-47b9-ba57-6f322b8f0da1 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: mmd7Ns3rT/xONI4WYwwKChje0Ye9XmE9n9PkAdheU7V1Ajh9DljCkAhwpZdodO7qfXCW7iQYQ+yESY2/XvXzkw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CH9PR11MB9904 X-Proofpoint-GUID: WDYAKL_PjxJuO5247lEiJH7oEMVkwfkM X-Proofpoint-ORIG-GUID: WDYAKL_PjxJuO5247lEiJH7oEMVkwfkM X-Proofpoint-Spam-Info: AW1haW4tMjYwNzMxMDA5MyBTYWx0ZWRfX3RaRILz3rsGb bq8bDY2gyWQuXJOCKiVI8qsA/vk0tFja67GVFYbPzk2Uf+mQMqD7lxPVFj0vBalV4+KlLrq1CaW 1euNT1kXBZK30wsu77PuwCpcqhRGfmE= X-Authority-Analysis: v=2.4 cv=Qq1uG1yd c=1 sm=1 tr=0 ts=6a6c9291 cx=c_pps a=mPJaQZadrGlgYT55Vo1ogA==:117 a=z/mQ4Ysz8XfWz/Q5cLBRGdckG28=:19 a=lCpzRmAYbLLaTzLvsPZ7Mbvzbb8=:19 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=x7bEGLp0ZPQA:10 a=fWIk7vUBimkA:10 a=VkNPw1HP01LnGYTKEx00:22 a=HuL5yKgYSaDc2Nh3iM7q:22 a=wdckrRGfQcVuOpxaH-Ii:22 a=dlfijevYFSpLufeOhYoA:9 a=CjuIK1q_8ugA:10 a=O8hF6Hzn-FEA:10 a=wI4udrOHAMoS7uN-7f0A:9 a=r0orhXcdyYkXbzw7:21 a=frz4AuCg-hUA:10 a=_W_S_7VecoQA:10 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzMxMDA5MyBTYWx0ZWRfX9FNgZBXGqBn2 8Q0fAFp6iApXAUhIpJOvWqAeXY+/sdy0GSHOmXWJV0Pz1d0ouY6BPUJefPXA4ZJlLTtrfzdTqPT 0BxtsaK3WJncDB9669jLFyI5tmtyK4nEEvp8OMtqzEX09ImTnmnQkAc3Wqi8OblV12yawW1N7Zx HHlPiVkq885dmf4P1IFU/aWgEuWTzl9vWwVY7leGvHuKVSk9c4l/DD9ZS//vfgqj3xA8ajL0ssP djxGf9f7LmkbLYQ9ImyABxm6UYuwRitB1eTEBgj8Owtkf18WXQjDZh426ClXZJtDS3G6He9T08w 9qzQO7L/aW2w6qwVR13LAz/gWa5ZvWBeKyklO3dONBRNedk+gGwRJVn4mcfC9uGPEohKRAx62c0 E9j77OhMkf1MAOE2v0odKcn5CwIvDnuzm7af/+Z9YDQO49yUnLRCccFhPmCD1uM3a7o7Bgn5eKv l2axVJc901xQa8uN3mw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-31_04,2026-07-30_01,2025-10-01_01 X-MailFrom: rlawrence@tamu.edu X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation Message-ID-Hash: ASAR5PD7E4EJNPEU3BF6PGEHVHGNDF2L X-Message-ID-Hash: ASAR5PD7E4EJNPEU3BF6PGEHVHGNDF2L X-Mailman-Approved-At: Fri, 31 Jul 2026 20:11:06 +0200 X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: --_000_DM3PR11MB871373A3A2D6F1F07916FE5DDAC82DM3PR11MB8713namp_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Clang-tidy caught two problems in snapshot_initial_fds(). A new patch versi= on will be sent. * Unsafe atoi to be replaced with strtol * Unsafe realloc result to be checked before assignment to arr, not after ________________________________ From: Richard Lawrence Sent: Friday, July 31, 2026 1:03 AM To: passt-dev@passt.top Cc: Lawrence, Richard E Subject: [PATCH] feat: Pass open files to child in pasta mode When pasta mode is used to launch an executable (`pasta [COMMAND]`) and that executable accepts inputs in the form of arbitrary file descriptors (such as `bwrap`), then passt should not stand in the way of the parent process handing off those file descriptors to the child process. See bug 204 for additional discussion. Changes: - `conf_tap_fd()` is now called directly by `conf()`. - The tap fd is relocated to a number at least 3, rather than exactly 3. - `snapshot_initial_fds()` memorizes inherited fds early in startup. - Inherited fds are discovered by reading from `/proc` if available. - `isolate_fds()` is now called after `conf()` in `main()`. Signed-off-by: Richard Lawrence --- conf.c | 39 +++++++++------- conf.h | 1 - isolation.c | 128 +++++++++++++++++++++++++++++++++++----------------- isolation.h | 9 +++- passt.c | 7 ++- 5 files changed, 122 insertions(+), 62 deletions(-) diff --git a/conf.c b/conf.c index faf2681..6bfdfb6 100644 --- a/conf.c +++ b/conf.c @@ -1169,26 +1169,20 @@ static void conf_sock_listen(const struct ctx *c) } /** - * conf_tap_fd() - Read tap fd as supplied by -F command line option - * @argc: Argument count - * @argv: Command line options + * conf_tap_fd() - Read and relocate tap fd as supplied by -F command line= option + * @fdarg: String containing fd + * + * Should: + * - move the --fd descriptor out of the range 0-2 * * Return: fd number from --fd option, or -1 if not supplied */ -int conf_tap_fd(int argc, char **argv) +int conf_tap_fd(const char *fdarg) { - const struct option optfd[] =3D { { "fd", required_argument, NULL, = 'F' }, - { 0 }, }; - const char *fdarg =3D NULL, *p; unsigned long val; - int name; - - optind =3D 0; - do { - name =3D getopt_long(argc, argv, "-:F:", optfd, NULL); - if (name =3D=3D 'F') - fdarg =3D optarg; - } while (name !=3D -1); + const char *p; + int new_fd; + int fd; if (!fdarg) return -1; @@ -1197,7 +1191,18 @@ int conf_tap_fd(int argc, char **argv) if (!parse_unsigned(&p, 0, &val) || !parse_eoi(p) || val > INT_MAX= ) die("Invalid --fd: %s", fdarg); - return val; + fd =3D (int)val; + if (fd >=3D 0 && fd < 3) { + new_fd =3D fcntl(fd, F_DUPFD, 3); + + if (new_fd < 0) + die_perror("Could not relocate --fd descriptor"); + + close(fd); + fd =3D new_fd; + } + + return fd; } /** @@ -1625,7 +1630,7 @@ void conf(struct ctx *c, int argc, char **argv) c->fd_control_listen =3D c->fd_control =3D -1; break; case 'F': - /* --fd was parsed early and c->fd_tap set in main(= ) */ + c->fd_tap =3D conf_tap_fd(optarg); c->one_off =3D true; *c->sock_path =3D 0; break; diff --git a/conf.h b/conf.h index 19bf9bc..16f9718 100644 --- a/conf.h +++ b/conf.h @@ -7,7 +7,6 @@ #define CONF_H enum passt_modes conf_mode(int argc, char *argv[]); -int conf_tap_fd(int argc, char **argv); void conf(struct ctx *c, int argc, char **argv); void conf_listen_handler(struct ctx *c, uint32_t events); void conf_handler(struct ctx *c, uint32_t events); diff --git a/isolation.c b/isolation.c index 94cbe7f..bedc4ce 100644 --- a/isolation.c +++ b/isolation.c @@ -24,19 +24,13 @@ * done anything we need to do with those resources, so we have * multiple stages of self-isolation. In order these are: * - * 1a. isolate_initial() + * 1. isolate_initial() * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * * Executed immediately after startup, drops capabilities we don't * need at any point during execution (or which we gain back when we * need by joining other namespaces). * - * 1b. isolate_fds() - * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - * - * Executed immediately after isolate_initial(). Closes any leaked - * files we might have inherited from the parent process. - * * 2. isolate_user() * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * @@ -44,14 +38,20 @@ * operate in. Sets our final UID & GID, and enters the correct user * namespace. * - * 3. isolate_prefork() + * 3. isolate_fds() + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Executed after conf(). Closes any leaked + * files we might have inherited from the parent process. + * + * 4. isolate_prefork() * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * * Executed after all setup, but before daemonising (fork()ing into * the background). Uses mount namespace and pivot_root() to remove * our access to the filesystem. * - * 4. isolate_postfork() + * 5. isolate_postfork() * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D * * Executed immediately after daemonizing, but before entering the @@ -61,6 +61,7 @@ * runtime operation. */ +#include #include #include #include @@ -77,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -248,47 +250,89 @@ void isolate_initial(void) drop_caps_ep_except(keep); } -/* - * isolate_fds() - Close leaked files, but not --fd, stdin, stdout, stderr - * @argc: Argument count - * @argv: Command line options, as we need to skip any file given via= --fd - * - * Should: - * - close all open files except for standard streams and the one from --= fd - * - move the --fd descriptor out of the range 0-2 - * - * Return: new fd number for descriptor from --fd, or -1 if not specified +/** + * snapshot_initial_fds() - Snapshot initial file descriptors inherited fr= om parent + * other than standard streams (stdin, stdout, stderr) + * @ifds: Snapshot struct of initial file descriptors to populate */ -int isolate_fds(int argc, char **argv) +void snapshot_initial_fds(struct initial_fd_snapshot *ifds) { - int fd, close_from =3D STDERR_FILENO + 1; + struct dirent *entry; + size_t capacity =3D 16; + int max_fd =3D 1024; + struct rlimit rl; + int dir_fd; + DIR *dir; + int fd; + + ifds->arr =3D NULL; + ifds->count =3D 0; + + dir =3D opendir("/proc/self/fd"); + if (dir) { + dir_fd =3D dirfd(dir); + ifds->arr =3D malloc(sizeof(int) * capacity); + if (!ifds->arr) + die_perror("Failed to allocate memory for inherited= fds"); + + while ((entry =3D readdir(dir)) !=3D NULL) { + if (entry->d_name[0] < '0' || entry->d_name[0] > '9= ') + continue; + + fd =3D atoi(entry->d_name); + /* Ignore stdin/stdout/stderr and the opendir handl= e itself */ + if (fd > STDERR_FILENO && fd !=3D dir_fd) { + if (ifds->count >=3D capacity) { + capacity *=3D 2; + ifds->arr =3D realloc(ifds->arr, + sizeof(int) * c= apacity); + if (!ifds->arr) + die_perror("Failed to reall= ocate memory for inherited fds"); + } + ifds->arr[ifds->count++] =3D fd; + } + } + closedir(dir); + return; + } + + /* Fallback for environments without /proc (e.g. minimal chroots) *= / + if (getrlimit(RLIMIT_NOFILE, &rl) =3D=3D 0 && rl.rlim_cur !=3D RLIM= _INFINITY) + max_fd =3D (int)rl.rlim_cur; - fd =3D conf_tap_fd(argc, argv); + ifds->arr =3D malloc(sizeof(int) * max_fd); + if (!ifds->arr) + die_perror("Failed to allocate memory for inherited fds fal= lback"); - if (fd >=3D 0) { - /* Move the passed fd to a more convenient location */ - if (fd !=3D close_from && - (dup2(fd, close_from) !=3D close_from || - close(fd))) - die_perror("Could not move --fd descriptor"); - fd =3D close_from++; + for (fd =3D STDERR_FILENO + 1; fd < max_fd; fd++) { + if (fcntl(fd, F_GETFD) >=3D 0) + ifds->arr[ifds->count++] =3D fd; } +} - if (close_range(close_from, ~0U, CLOSE_RANGE_UNSHARE)) { - if (errno =3D=3D ENOSYS || errno =3D=3D EINVAL) { - /* This probably means close_range() or the - * CLOSE_RANGE_UNSHARE flag is not supported by the - * kernel. Not much we can do here except carry on= and - * hope for the best. - */ - warn( -"Can't use close_range() to ensure no files leaked by parent"); - } else { - die_perror("Failed to close files leaked by parent"= ); - } +/** + * isolate_fds() - Close leaked files from the parent process + * @ifds: Snapshot of initial file descriptors + * @keep_fd: File descriptor to keep open, if any + * + * Should: + * - close all file descriptors that were open at startup, except for kee= p_fd + */ +void isolate_fds(struct initial_fd_snapshot *ifds, int keep_fd) +{ + size_t i; + + if (!ifds || !ifds->arr) + return; + + for (i =3D 0; i < ifds->count; i++) { + if (ifds->arr[i] !=3D keep_fd) + close(ifds->arr[i]); } - return fd; + free(ifds->arr); + ifds->arr =3D NULL; + ifds->count =3D 0; } /** diff --git a/isolation.h b/isolation.h index ec47038..8ada988 100644 --- a/isolation.h +++ b/isolation.h @@ -8,10 +8,17 @@ #define ISOLATION_H #include +#include #include +struct initial_fd_snapshot { + int *arr; + size_t count; +}; + void isolate_initial(void); -int isolate_fds(int argc, char **argv); +void snapshot_initial_fds(struct initial_fd_snapshot *ifds); +void isolate_fds(struct initial_fd_snapshot *ifds, int keep_fd); void isolate_user(const struct ctx *c, uid_t uid, gid_t gid, bool use_user= ns, const char *userns); int isolate_prefork(const struct ctx *c); diff --git a/passt.c b/passt.c index 5054551..c4335bf 100644 --- a/passt.c +++ b/passt.c @@ -335,6 +335,7 @@ int main(int argc, char **argv) struct epoll_event events[NUM_EPOLL_EVENTS]; int nfds, devnull_fd =3D -1, fd; struct ctx *c =3D &passt_ctx; + struct initial_fd_snapshot ifds; struct rlimit limit; struct timespec now; struct sigaction sa; @@ -344,8 +345,9 @@ int main(int argc, char **argv) arch_avx2_exec(argv); + snapshot_initial_fds(&ifds); + isolate_initial(); - c->fd_tap =3D isolate_fds(argc, argv); if ((devnull_fd =3D open("/dev/null", O_RDWR | O_CLOEXEC)) < 0) die_perror("Failed to open /dev/null"); @@ -390,6 +392,9 @@ int main(int argc, char **argv) sock_probe_features(c); conf(c, argc, argv); + + isolate_fds(&ifds, c->fd_tap); + trace_init(c->trace); pasta_netns_quit_init(c); -- 2.52.0 --_000_DM3PR11MB871373A3A2D6F1F07916FE5DDAC82DM3PR11MB8713namp_ Content-Type: text/html; charset="us-ascii" Content-Transfer-Encoding: quoted-printable
Clang-tidy caught two problems in snapshot_initial_fds(). A ne= w patch version will be sent.

  • Unsafe atoi to be replaced with strtol 
  • Unsafe realloc result to be checked before assignment to arr, not after 

From: Richard Lawrence <= rlawrence@tamu.edu>
Sent: Friday, July 31, 2026 1:03 AM
To: passt-dev@passt.top <passt-dev@passt.top>
Cc: Lawrence, Richard E <rlawrence@tamu.edu>
Subject: [PATCH] feat: Pass open files to child in pasta mode
 
When pasta mode is used to launch an executable (`= pasta [COMMAND]`) and
that executable accepts inputs in the form of arbitrary file descriptors (such as `bwrap`), then passt should not stand in the way of the parent
process handing off those file descriptors to the child process.
See bug 204 for additional discussion.

Changes:
- `conf_tap_fd()` is now called directly by `conf()`.
- The tap fd is relocated to a number at least 3, rather than exactly 3. - `snapshot_initial_fds()` memorizes inherited fds early in startup.
- Inherited fds are discovered by reading from `/proc` if available.
- `isolate_fds()` is now called after `conf()` in `main()`.

Signed-off-by: Richard Lawrence <rlawrence@tamu.edu>
---
 conf.c      |  39 +++++++++-------
 conf.h      |   1 -
 isolation.c | 128 +++++++++++++++++++++++++++++++++++----------------= -
 isolation.h |   9 +++-
 passt.c     |   7 ++-
 5 files changed, 122 insertions(+), 62 deletions(-)

diff --git a/conf.c b/conf.c
index faf2681..6bfdfb6 100644
--- a/conf.c
+++ b/conf.c
@@ -1169,26 +1169,20 @@ static void conf_sock_listen(const struct ctx *c)  }
 
 /**
- * conf_tap_fd() - Read tap fd as supplied by -F command line option
- * @argc:      Argument count
- * @argv:      Command line options
+ * conf_tap_fd() - Read and relocate tap fd as supplied by -F command line= option
+ * @fdarg:     String containing fd
+ *
+ * Should:
+ *  - move the --fd descriptor out of the range 0-2
  *
  * Return: fd number from --fd option, or -1 if not supplied
  */
-int conf_tap_fd(int argc, char **argv)
+int conf_tap_fd(const char *fdarg)
 {
-       const struct option optfd[] =3D { { &= quot;fd", required_argument, NULL, 'F' },
-            &n= bsp;            = ;            &n= bsp; { 0 }, };
-       const char *fdarg =3D NULL, *p;
         unsigned long val;
-       int name;
-
-       optind =3D 0;
-       do {
-            &n= bsp;  name =3D getopt_long(argc, argv, "-:F:", optfd, NULL);=
-            &n= bsp;  if (name =3D=3D 'F')
-            &n= bsp;          fdarg =3D optarg= ;
-       } while (name !=3D -1);
+       const char *p;
+       int new_fd;
+       int fd;
 
         if (!fdarg)
            &nb= sp;    return -1;
@@ -1197,7 +1191,18 @@ int conf_tap_fd(int argc, char **argv)
         if (!parse_unsigned(&p= , 0, &val) || !parse_eoi(p) || val > INT_MAX)
            &nb= sp;    die("Invalid --fd: %s", fdarg);
 
-       return val;
+       fd =3D (int)val;
+       if (fd >=3D 0 && fd < 3= ) {
+            &n= bsp;  new_fd =3D fcntl(fd, F_DUPFD, 3);
+
+            &n= bsp;  if (new_fd < 0)
+            &n= bsp;          die_perror("= ;Could not relocate --fd descriptor");
+
+            &n= bsp;  close(fd);
+            &n= bsp;  fd =3D new_fd;
+       }
+
+       return fd;
 }
 
 /**
@@ -1625,7 +1630,7 @@ void conf(struct ctx *c, int argc, char **argv)
            &nb= sp;            c->= ;fd_control_listen =3D c->fd_control =3D -1;
            &nb= sp;            break= ;
            &nb= sp;    case 'F':
-            &n= bsp;          /* --fd was pars= ed early and c->fd_tap set in main() */
+            &n= bsp;          c->fd_tap =3D= conf_tap_fd(optarg);
            &nb= sp;            c->= ;one_off =3D true;
            &nb= sp;            *c-&g= t;sock_path =3D 0;
            &nb= sp;            break= ;
diff --git a/conf.h b/conf.h
index 19bf9bc..16f9718 100644
--- a/conf.h
+++ b/conf.h
@@ -7,7 +7,6 @@
 #define CONF_H
 
 enum passt_modes conf_mode(int argc, char *argv[]);
-int conf_tap_fd(int argc, char **argv);
 void conf(struct ctx *c, int argc, char **argv);
 void conf_listen_handler(struct ctx *c, uint32_t events);
 void conf_handler(struct ctx *c, uint32_t events);
diff --git a/isolation.c b/isolation.c
index 94cbe7f..bedc4ce 100644
--- a/isolation.c
+++ b/isolation.c
@@ -24,19 +24,13 @@
  * done anything we need to do with those resources, so we have
  * multiple stages of self-isolation.  In order these are:
  *
- * 1a. isolate_initial()
+ * 1. isolate_initial()
  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
  *
  * Executed immediately after startup, drops capabilities we don't   * need at any point during execution (or which we gain back when we<= br>   * need by joining other namespaces).
  *
- * 1b. isolate_fds()
- * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
- *
- * Executed immediately after isolate_initial().  Closes any leaked - * files we might have inherited from the parent process.
- *
  * 2. isolate_user()
  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
  *
@@ -44,14 +38,20 @@
  * operate in.  Sets our final UID & GID, and enters the cor= rect user
  * namespace.
  *
- * 3. isolate_prefork()
+ * 3. isolate_fds()
+ * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
+ *
+ * Executed after conf().  Closes any leaked
+ * files we might have inherited from the parent process.
+ *
+ * 4. isolate_prefork()
  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
  *
  * Executed after all setup, but before daemonising (fork()ing into   * the background).  Uses mount namespace and pivot_root() to re= move
  * our access to the filesystem.
  *
- * 4. isolate_postfork()
+ * 5. isolate_postfork()
  * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D   *
  * Executed immediately after daemonizing, but before entering the @@ -61,6 +61,7 @@
  * runtime operation.
  */
 
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
@@ -77,6 +78,7 @@
 #include <unistd.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -248,47 +250,89 @@ void isolate_initial(void)
         drop_caps_ep_except(keep);=
 }
 
-/*
- * isolate_fds() - Close leaked files, but not --fd, stdin, stdout, stderr=
- * @argc:      Argument count
- * @argv:      Command line options, as we need t= o skip any file given via --fd
- *
- * Should:
- *  - close all open files except for standard streams and the one fr= om --fd
- *  - move the --fd descriptor out of the range 0-2
- *
- * Return: new fd number for descriptor from --fd, or -1 if not specified<= br> +/**
+ * snapshot_initial_fds() - Snapshot initial file descriptors inherited fr= om parent
+ * other than standard streams (stdin, stdout, stderr)
+ * @ifds:      Snapshot struct of initial file de= scriptors to populate
  */
-int isolate_fds(int argc, char **argv)
+void snapshot_initial_fds(struct initial_fd_snapshot *ifds)
 {
-       int fd, close_from =3D STDERR_FILENO = + 1;
+       struct dirent *entry;
+       size_t capacity =3D 16;
+       int max_fd =3D 1024;
+       struct rlimit rl;
+       int dir_fd;
+       DIR *dir;
+       int fd;
+
+       ifds->arr =3D NULL;
+       ifds->count =3D 0;
+
+       dir =3D opendir("/proc/self/fd&q= uot;);
+       if (dir) {
+            &n= bsp;  dir_fd =3D dirfd(dir);
+            &n= bsp;  ifds->arr =3D malloc(sizeof(int) * capacity);
+            &n= bsp;  if (!ifds->arr)
+            &n= bsp;          die_perror("= ;Failed to allocate memory for inherited fds");
+
+            &n= bsp;  while ((entry =3D readdir(dir)) !=3D NULL) {
+            &n= bsp;          if (entry->d_= name[0] < '0' || entry->d_name[0] > '9')
+            &n= bsp;            = ;      continue;
+
+            &n= bsp;          fd =3D atoi(entr= y->d_name);
+            &n= bsp;          /* Ignore stdin/= stdout/stderr and the opendir handle itself */
+            &n= bsp;          if (fd > STDE= RR_FILENO && fd !=3D dir_fd) {
+            &n= bsp;            = ;      if (ifds->count >=3D capacity) {
+            &n= bsp;            = ;            &n= bsp; capacity *=3D 2;
+            &n= bsp;            = ;            &n= bsp; ifds->arr =3D realloc(ifds->arr,
+            &n= bsp;            = ;            &n= bsp;            = ;         sizeof(int) * capacity);<= br> +            &n= bsp;            = ;            &n= bsp; if (!ifds->arr)
+            &n= bsp;            = ;            &n= bsp;         die_perror("Faile= d to reallocate memory for inherited fds");
+            &n= bsp;            = ;      }
+            &n= bsp;            = ;      ifds->arr[ifds->count++] =3D fd;
+            &n= bsp;          }
+            &n= bsp;  }
+            &n= bsp;  closedir(dir);
+            &n= bsp;  return;
+       }
+
+       /* Fallback for environments without = /proc (e.g. minimal chroots) */
+       if (getrlimit(RLIMIT_NOFILE, &rl)= =3D=3D 0 && rl.rlim_cur !=3D RLIM_INFINITY)
+            &n= bsp;  max_fd =3D (int)rl.rlim_cur;
 
-       fd =3D conf_tap_fd(argc, argv);
+       ifds->arr =3D malloc(sizeof(int) *= max_fd);
+       if (!ifds->arr)
+            &n= bsp;  die_perror("Failed to allocate memory for inherited fds fal= lback");
 
-       if (fd >=3D 0) {
-            &n= bsp;  /* Move the passed fd to a more convenient location */
-            &n= bsp;  if (fd !=3D close_from       =              &a= mp;&
-            &n= bsp;      (dup2(fd, close_from) !=3D close_from&nb= sp; ||
-            &n= bsp;       close(fd)))
-            &n= bsp;          die_perror("= ;Could not move --fd descriptor");
-            &n= bsp;  fd =3D close_from++;
+       for (fd =3D STDERR_FILENO + 1; fd <= ; max_fd; fd++) {
+            &n= bsp;  if (fcntl(fd, F_GETFD) >=3D 0)
+            &n= bsp;          ifds->arr[ifd= s->count++] =3D fd;
         }
+}
 
-       if (close_range(close_from, ~0U, CLOS= E_RANGE_UNSHARE)) {
-            &n= bsp;  if (errno =3D=3D ENOSYS || errno =3D=3D EINVAL) {
-            &n= bsp;          /* This probably= means close_range() or the
-            &n= bsp;           * CLOSE_RA= NGE_UNSHARE flag is not supported by the
-            &n= bsp;           * kernel.&= nbsp; Not much we can do here except carry on and
-            &n= bsp;           * hope for= the best.
-            &n= bsp;           */
-            &n= bsp;          warn(
-"Can't use close_range() to ensure no files leaked by parent");<= br> -            &n= bsp;  } else {
-            &n= bsp;          die_perror("= ;Failed to close files leaked by parent");
-            &n= bsp;  }
+/**
+ * isolate_fds() - Close leaked files from the parent process
+ * @ifds:      Snapshot of initial file descripto= rs
+ * @keep_fd:   File descriptor to keep open, if any
+ *
+ * Should:
+ *  - close all file descriptors that were open at startup, except fo= r keep_fd
+ */
+void isolate_fds(struct initial_fd_snapshot *ifds, int keep_fd)
+{
+       size_t i;
+
+       if (!ifds || !ifds->arr)
+            &n= bsp;  return;
+
+       for (i =3D 0; i < ifds->count; = i++) {
+            &n= bsp;  if (ifds->arr[i] !=3D keep_fd)
+            &n= bsp;          close(ifds->a= rr[i]);
         }
 
-       return fd;
+       free(ifds->arr);
+       ifds->arr =3D NULL;
+       ifds->count =3D 0;
 }
 
 /**
diff --git a/isolation.h b/isolation.h
index ec47038..8ada988 100644
--- a/isolation.h
+++ b/isolation.h
@@ -8,10 +8,17 @@
 #define ISOLATION_H
 
 #include <stdbool.h>
+#include <stddef.h>
 #include <unistd.h>
 
+struct initial_fd_snapshot {
+       int *arr;
+       size_t count;
+};
+
 void isolate_initial(void);
-int isolate_fds(int argc, char **argv);
+void snapshot_initial_fds(struct initial_fd_snapshot *ifds);
+void isolate_fds(struct initial_fd_snapshot *ifds, int keep_fd);
 void isolate_user(const struct ctx *c, uid_t uid, gid_t gid, bool use= _userns,
            &nb= sp;      const char *userns);
 int isolate_prefork(const struct ctx *c);
diff --git a/passt.c b/passt.c
index 5054551..c4335bf 100644
--- a/passt.c
+++ b/passt.c
@@ -335,6 +335,7 @@ int main(int argc, char **argv)
         struct epoll_event events[= NUM_EPOLL_EVENTS];
         int nfds, devnull_fd =3D -= 1, fd;
         struct ctx *c =3D &pas= st_ctx;
+       struct initial_fd_snapshot ifds;
         struct rlimit limit;
         struct timespec now;
         struct sigaction sa;
@@ -344,8 +345,9 @@ int main(int argc, char **argv)
 
         arch_avx2_exec(argv);
 
+       snapshot_initial_fds(&ifds);
+
         isolate_initial();
-       c->fd_tap =3D isolate_fds(argc, ar= gv);
 
         if ((devnull_fd =3D open(&= quot;/dev/null", O_RDWR | O_CLOEXEC)) < 0)
            &nb= sp;    die_perror("Failed to open /dev/null");
@@ -390,6 +392,9 @@ int main(int argc, char **argv)
         sock_probe_features(c);  
         conf(c, argc, argv);
+
+       isolate_fds(&ifds, c->fd_tap);=
+      
         trace_init(c->trace);  
         pasta_netns_quit_init(c);<= br> --
2.52.0

--_000_DM3PR11MB871373A3A2D6F1F07916FE5DDAC82DM3PR11MB8713namp_--