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=Z0W7Rcyh; dkim-atps=neutral Received: from mx0b-00178102.pphosted.com (mx0b-00178102.pphosted.com [148.163.139.245]) by passt.top (Postfix) with ESMTPS id 5B5CE5A026D for ; Sat, 18 Jul 2026 18:52:55 +0200 (CEST) Received: from pps.filterd (m0231226.ppops.net [127.0.0.1]) by mx0b-00178102.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66IGq51D647126; Sat, 18 Jul 2026 11:52:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tamu.edu; h=cc :content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to; s=ppae6d7b; bh=A3v2UIkffDXWIUteRAHnuXKpy UXDb574WQ735r8vcAo=; b=Z0W7RcyhiDLuen9cEBjOZK/KgldC8vNl7Jj87kiW2 evNgpi5RbcsKtFclx2ytjleoqb7e/uNCpGg7BEu0jRSX+QO1CAQHgWliwxvCLV+m eoXRN93v8z4QQDOpuaR/PLjpRkR+regZDou1o850SW1lX0+nfIgHQcwQBqZL/OrQ 4Wt4MRdomzDU+Dtkrz3nub5VoqyNxNAuUYQ9Pn76/sCf2qYCLH/tB3T6qhPZBBLg DKJKoxFRkLY8qVBoaIG/0u+P4tNB2SJ04lX4AKOJ9Mg9Uqb9CH/XxPEKfN/VAws/ JC0uiuN+wJTFGEgtIXLL+XIqkIgo6ZN3hrGzDKv5gUTGg== Received: from sa9pr02cu001.outbound.protection.outlook.com (mail-southcentralusazon11013018.outbound.protection.outlook.com [40.93.196.18]) by mx0b-00178102.pphosted.com (PPS) with ESMTPS id 4fg8mjsjaf-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Sat, 18 Jul 2026 11:52:50 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=n70LJHbk9gnBSNiPX3LCkPl5ds7QaJerIlsO1mMncsKPzc6CaCcV2yI6xwNJs6JI7UtNQl/Aoq/90WTWmgrJXzDQjxIawHZq7qocryrNWGU3xYV3MhZkeFlNlvU2PKOoDLDj3j27+lWCzM7A3xV1ch7yqzBGhDnw+A8zhfIrNexHFK6uyvToxvZoSmSbPEV+VVDFR6G4k7tD9hPW0i21ObTIu8qGgWsCwGy/GoabsuEpr+qm5a/7dMcIxFaSlPGtmCve4ej2gog59hgIltRSfnkFGU5qj2cWZKncRX476sKoV4ANoUdFv8Lvn9ZElgZ/ePGsjGZmo5coM5xHpzBjcA== 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=A3v2UIkffDXWIUteRAHnuXKpyUXDb574WQ735r8vcAo=; b=Y0gB5EaqkCE4Pjs9lLUYAH8sb2Picl1gdPNTJM/8M2uzaz+Y0bZOYLObBmvewwwAsfxzZLeGFZxdCejDiw23cJtvHwiUaTX5Dzwj2pWh/IvURZSNRzX05i9+cOIFr+9NOf4TJuJbrlYc+9pubUaQnHFFhSw00FJTl0Oq1k77oJpoLl7KywhhrKmieQZvbOag5eG+nBRYuYhnWuOTCSsAg5b0TwFvfzglK7DbmCug/xotcutt0TbkbP883kbrBmPN1zORxG9BCsVEgnatQUxjqRM92GXNxhlObWbCkcJ1cYh8txQMcDUVK6O+eU5z2nlnh2uHLhc+zp3Y+AcD/2aVjw== 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 SN7PR11MB6828.namprd11.prod.outlook.com (2603:10b6:806:2a3::17) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.223.15; Sat, 18 Jul 2026 16:52:46 +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.0223.013; Sat, 18 Jul 2026 16:52:46 +0000 From: "Lawrence, Richard E" To: "passt-dev@passt.top" Subject: Re: [PATCH v2] feat: Add cli option '--pass-fds' for pasta mode. Thread-Topic: [PATCH v2] feat: Add cli option '--pass-fds' for pasta mode. Thread-Index: AQHdFtCKDa22cntO2kKEI5FH0THnEbZze44X Date: Sat, 18 Jul 2026 16:52:46 +0000 Message-ID: References: <20260718161429.173494-1-rlawrence@tamu.edu> In-Reply-To: <20260718161429.173494-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_|SN7PR11MB6828:EE_ x-ms-office365-filtering-correlation-id: 3bbc65f5-3b8d-4cf8-da3f-08dee4ecfe63 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0;ARA:13230040|786006|376014|23010399003|366016|1800799024|38070700021|56012099006|10067099003|11063799006|18002099003|22082099003|8096899003; x-microsoft-antispam-message-info: 7RyMM1bdHr8AlPyJjpmAmhK16/o/8gaSd/n3FNuNaN0WOnw/obGE/X9WD8OQIEqtFbhNJPv7H9CgWaZY7mZzTwiDmac355Q8w37upp2hGpF93Lyvkd07sU+NsACXnpWj798usmY+Mqhldu555BmbjlWA5ql+MFMEBM/Nb8Jgq081lZo94EJuK7DMQYwHpZV1vx//jxf9MB9fkiBfqqulHvHQK5SxeXLQ0+kX/uuyQrTwqxTsyKIbbs24OOJrueuvLBozpSrlMzMlcQv0IVnR7ESKtNmbhh5ZwwmhgaLFxZqWYf/a7o5mF/WZ4f1e8nJ2cQQJyrREvLnjUgihmyZ6B0lzvevbWh6efu13KOFqzNwxNLpQ/6gA0wJRCuemW5iblUky+ZjX94yptOkW42HpqHIbBEw9N3rhJRteKqEgbQeebYYM3R5iJJukT81fq7d1v49uoX2UbHjvtSElgSDfUD7Mip0mmibsnxgoRcD9X04ksRmALdljc0ZaaTv63jjDQIKQNr8q44NBMZq2Y6hGmBgRMkVLhodFVCwGZ2zZEgnHkT9AKw0iEZvh3QfxvxT9IcYFvI+1CgYEDjGbsCb/JgLtd422TpNtFrRIImL2V3AQAaEHguEIPmIWnNBuRmwKSPzaZoPfQRgMsN25L2Amnt6kT1pqEhfW8HeC3ni0cHIWffULUiPNHsMJOlz4X1TTYAPbN/tE5lef5o3mNxREGOO8O7XgYlOu9SDNdwxUg+k= 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)(786006)(376014)(23010399003)(366016)(1800799024)(38070700021)(56012099006)(10067099003)(11063799006)(18002099003)(22082099003)(8096899003);DIR:OUT;SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?u3v1WSi7WH6Ja9vOaPTP6r7s5pcE16ASzqJuoCbjIzL7Pr/mT8vPnM/Gc764?= =?us-ascii?Q?avGiumblQfxRnXY1vVkaEit8T52LhgXKF0ia3gFEQEufgGQi2j5Xl3Ds6ow8?= =?us-ascii?Q?ZSwyd4QYWV01CSeJhiou4BIBqTHEfahpwrX6jh2cBU3i8XrpI/z4uxYpm3B5?= =?us-ascii?Q?eeRTslBT8LTLRJ4ZOPIYgoWxCp7dQrtJKv/Xod4IVlrnDF54TWUBR3FiU1As?= =?us-ascii?Q?l+n0giI7WGJM9xr9fCWkZncoUplChjoVHsIJIA7yaNq5blBQt0F66m72Yn8g?= =?us-ascii?Q?KkxWu21uzNC6E8qpdOWvrG60oUXsvJr/zieae6K+vbsAYTrDZQLbSRy7unSx?= =?us-ascii?Q?8Gs6rmsUD5mpl11kcvgpTCfs+NofHHEa13uAK7yec+HEWwznIu0QGRNSWjqj?= =?us-ascii?Q?c1aoJzYEtzEFtvNJeKfepS/RcqcZ7UBmBBZjjc7T+Q7NG8EaALLlRUGZUW4b?= =?us-ascii?Q?3341lk9SVCdZWskPq990jPFPkEhBSU8RX9J2Ug/4ncQcVfiosiOwGesARX0v?= =?us-ascii?Q?qXP5jblJ5ntsiBpa75jUqVVHGhHkZReV/5uOEnPVcH6TcQLNi6DA0nqIB4bZ?= =?us-ascii?Q?WcY1ynL0MkpvBeUnuDd50snK5yHCLYBJn31YJJRh3Tydw/U7s050WUdSIlqh?= =?us-ascii?Q?Q8O0X4B96u6mSCpRimIFupjb9Mb3mLGb39p8v4QIk8yDkywLQlGm8u8y8LlY?= =?us-ascii?Q?mUZeYOHSk69WBY2Dt15qvbAmW8DQ1NGOWntgp/wFLmb0a3hwt6pLBpdppB/Y?= =?us-ascii?Q?OzpdXZCRjVt5NXHdRk/fmT8OMU1D65JQ8jVJB9qt9gEggO6ElAzP+iUqmNse?= =?us-ascii?Q?9L3eB75LWZt0wuuMZUwyxPB9yCZjZqspBjUOzEP3JdUf6HuFDCK9f9uJgXcn?= =?us-ascii?Q?SnRKTv0UYbXxqG8xNcm69z8sq4aheOmxFc9q0xeGUmw9l6Vm6+m6Pktba4+N?= =?us-ascii?Q?YVn8Aq2CMwQeLnjmmsiuad/5l2+kEUqRYJwTlS/Ulup9nDc4EYwBbJb+Oq2P?= =?us-ascii?Q?OVK9WQCW0RNTKiF74CdsUAGy5d2ukcu67bxpjPRLgVFV/z/3OS1HO7fUSvgs?= =?us-ascii?Q?e+mttfEJEzkqc+j+ZPIGUH/Ft9KANwg5RvW8ZO7/L6zuEEuaZdVGfCOhaMPf?= =?us-ascii?Q?ecQhNC7oX0+pEuyVpEDjDwMGYTyOqo9TSq5BxpYSLyuMNZpxXAe6uKUGhh5y?= =?us-ascii?Q?Ibfg4Ews23D//Aibd1FhSNzdODMg/dYRPYd3j7uB4/zhp9KlgTkdxo1xwy5i?= =?us-ascii?Q?sN7x8dOpEvES7yC8E3XIzGIWglQ32PkqcXY4Tk+a0/0CCcwZSHJ8U2NALmQW?= =?us-ascii?Q?nlyOcQFlXTznGdzXa0PgIAk0IkQxd9UdLeS7XPlRwOQosJNd3jluHRkjlPTH?= =?us-ascii?Q?xuZ0kDy3jbuQld0geVs7vGouO/nBDDpKdEgj8xaaJqHFJqIqvuKU/NBAJNPH?= =?us-ascii?Q?UxOeJiZS5zF86sHVMDs/M4Ceslqorn01DPAj2XS5DUBEvaNK+0zNfb2heGfI?= =?us-ascii?Q?q3rZ9zdZUoP1E7p1N9mUo049/foexvqmipcRJcfIZACEjY65vRaBFiEy5wGB?= =?us-ascii?Q?g4+hu5GRw+K8Ju0Rm1bfVUl36C6NR9rF0fsF5WDeEeJh8O1+TuWgFhANtE0i?= =?us-ascii?Q?h0fTgNtrn3eZ00xVKulTJInihsvm7SMGFXSrx7MDzc1RUakGt5VuGlFZr7zK?= =?us-ascii?Q?lWqiSnCCJl4UzQ85fD5vwbbJp/ypR0//GvN+BKgapitzP1Kd?= Content-Type: multipart/alternative; boundary="_000_DM3PR11MB871351EF40D8980F4A54F8B7DAC52DM3PR11MB8713namp_" MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: OZoK36pn8sbGi7BmuQ6Z5y3cnnsIs+31eAdg3o1ZZUgy6EqSDKQjBt6Jr/GkkGo5bvN+NFJ6OaqqvHQcZNNWZ+mSAaYZdcrou/ZX2nesTRlHYzeGTHJTnncdb5w8awYzYKmyjS+Dj0nnGwniAAUvrwU9JTrxdj4fDGH2jYYvjEtdEHpRWvkmb9xwlFi67ZuGzxSPUWnMsjxte+QkueNVPCkr9QImKSr5E++8/MVeh3UcWNFjwSRq4DFrhp2TdCMB6AkG76c8N6aWopIjhTGGdTwvQSIXBSfYh8+0WqRNFQ+9PoUp68/Rh1ep5EFTwAH/B5JQdU6fRpOB4VLJqJmpJw== 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: 3bbc65f5-3b8d-4cf8-da3f-08dee4ecfe63 X-MS-Exchange-CrossTenant-originalarrivaltime: 18 Jul 2026 16:52:46.4106 (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: U2slUJctmLtWL4EzejoMc5N1lGeitRB1yW0dbstbKPBIQ+uJ0wEeojXKp9Xt76u+sJoEl7D3fmvO2eark3P0uw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN7PR11MB6828 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE4MDE3NiBTYWx0ZWRfXypkDb2AJrsEh tyudpCJwz8w46VTHDqWixBAqjCMuwVGWLckOY850KhpoZ+xcHF1sSirbGNl0QwtVLHB562659tM RkZ7HLk9ux4An+AWInyZ4mwRBnINDR4= X-Proofpoint-GUID: jzO8MG1zlpO1lrLmOprHYdsZ2XMR75sF X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE4MDE3NiBTYWx0ZWRfX3CHF9GgyAGiv Z3AL0ReWjmu+BYsvm+gGrpYer0gLt+y/dQOeqhdbJlC4bxovw2LFlN0+NpLvdlbybmFV+Qqhd5I 83J6/M1Ch92t4Ovyo/VSYhNz3jpHOdqHcFLuTbVGqVjm0+9VHwiZ2j+ypU8sYaX5e2YUri75h06 uPn9RoDgWOchiSm4ux8LiaINM7p+9HtKTVmGHuKmS9soJ5jjbYb4SetvKcYBOIS6JRbp6Z27jfd 33p+SGdH2kR35SrBMTIKCOtKtp5qTWAeGes0eIib8D344I/iNX5cC3Cd2zejDnBThTgRSOQQew2 pN2sNNcOXVlKgBJuY30egNdb1EHnYHOm/TOw/05NlKyDDBncn5ic+qCf95f6i6OSqsHgJj8j9Iv bB/XdBJCDudWvBXjVfVPwAq7YT9m8abBqhjLHKeYU1Qrvhf9TauV+FNZSnks/AXu/PhIzc9xtnS qaCmezJe8kvsS4KTr2Q== X-Proofpoint-ORIG-GUID: jzO8MG1zlpO1lrLmOprHYdsZ2XMR75sF X-Authority-Analysis: v=2.4 cv=CI0amxrD c=1 sm=1 tr=0 ts=6a5baf62 cx=c_pps a=H/ESeL7FTwDXaNlIHo07Cw==: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=7DFSJXxVznJstVW2-kzw:22 a=btVHSsReAAAA:8 a=M58lpiPW005r3OpKkXMA:9 a=CjuIK1q_8ugA:10 a=HHmkc113K0UCbmPNj3UA:9 a=IHdRp0bEBy8T7T-z:21 a=frz4AuCg-hUA:10 a=_W_S_7VecoQA:10 a=3UV5jaWp_vrj6uxwUC0B:22 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-18_05,2026-07-17_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: GSGKZG6D6L4OPCQYVX5AWQ7K235MQ3K5 X-Message-ID-Hash: GSGKZG6D6L4OPCQYVX5AWQ7K235MQ3K5 X-Mailman-Approved-At: Sun, 19 Jul 2026 17:03:40 +0200 CC: "jbash@jbash.com" 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_DM3PR11MB871351EF40D8980F4A54F8B7DAC52DM3PR11MB8713namp_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Nitpicks: * we use STDERR_FILENO to represent 2, so the target of fcntl should be STDER= R_FILENO + 1 rather than literal 3. * I think it would be clearer if prev_fd was declared but not initially defin= ed, and then set to STDERR_FILENO just before the loop, so that a comment l= ike /* keep standard streams */ would not be among a bunch of variable decl= arations, but would be located near where the variable is first used. * I think it would be clearer if min_fd was named next_fd by analogy to prev_= fd. ________________________________ From: Richard Lawrence Sent: Saturday, July 18, 2026 11:14 AM To: passt-dev@passt.top Cc: jbash@jbash.com ; Lawrence, Richard E ; Lawrence, Richard E Subject: [PATCH v2] feat: Add cli option '--pass-fds' for pasta mode. From: Richard Lawrence When pasta mode is used to launch an executable (`pasta [COMMAND]`) and tha= t 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 ad= ditional discussion. The `pass-fds` option accepts a comma-separated list of file descriptor numbers. `conf_pass_fds()` parses the command line argument, th= en `isolate_fds()` skips closing the specified fds by calling `close_range()` on the gaps between them. Additionally, the tap fd is safely relocated to the lowest unused fd number= which it at least 3, to avoid accidentally overwriting an existing fd. Signed-off-by: Richard Lawrence --- conf.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++- conf.h | 1 + isolation.c | 68 ++++++++++++++++++++++++++++++++++++++++++----------- passt.1 | 5 ++++ 4 files changed, 125 insertions(+), 15 deletions(-) diff --git a/conf.c b/conf.c index 0fcba5c..3246735 100644 --- a/conf.c +++ b/conf.c @@ -732,7 +732,9 @@ pasta_opts: " Don't copy all addresses to namesp= ace\n" " --ns-mac-addr ADDR Set MAC address on tap interface\n= " " --no-splice Disable inbound socket splicing\n" - " --splice-only Only enable loopback forwarding\n")= ; + " --splice-only Only enable loopback forwarding\n" + " --pass-fds FDS Comma-separated list of fds to pass= to\n" + " the spawned command\n"); passt_exit(status); } @@ -1183,6 +1185,63 @@ int conf_tap_fd(int argc, char **argv) return val; } +/** + * conf_pass_fds() - Read fds as supplied by --pass-fds command line optio= n + * @argc: Argument count + * @argv: Command line options + * @fds: Array where we store the parsed fds + * @max_fds: Maximum size of the array + * + * Return: number of parsed fds, or -1 if option not specified + */ +int conf_pass_fds(int argc, char **argv, int *fds, int max_fds) +{ + const struct option opt[] =3D { { "pass-fds", required_argument, NU= LL, 33 }, + { 0 }, }; + const char *fdsarg =3D NULL; + int name, fds_cnt =3D 0; + int old_opterr; + + old_opterr =3D opterr; + opterr =3D 0; + optind =3D 0; + do { + name =3D getopt_long(argc, argv, "-:", opt, NULL); + if (name =3D=3D 33) + fdsarg =3D optarg; + } while (name !=3D -1); + opterr =3D old_opterr; + + if (!fdsarg) + return -1; + + while (*fdsarg) { + unsigned long val; + char *endptr; + + val =3D strtoul(fdsarg, &endptr, 10); + if (fdsarg =3D=3D endptr) + die("Invalid --pass-fds option: %s", fdsarg); + + if (val > INT_MAX) + die("Invalid file descriptor in --pass-fds: %lu", v= al); + + if (fds_cnt >=3D max_fds) + die("Too many file descriptors in --pass-fds"); + + fds[fds_cnt++] =3D (int)val; + + if (*endptr =3D=3D ',') + fdsarg =3D endptr + 1; + else if (*endptr =3D=3D '\0') + fdsarg =3D endptr; + else + die("Invalid character in --pass-fds option: %s", f= dsarg); + } + + return fds_cnt; +} + /** * conf_addr() - Configure guest address with -a option * @c: Execution context @@ -1331,6 +1390,7 @@ void conf(struct ctx *c, int argc, char **argv) {"stats", required_argument, NULL, 31= }, {"conf-path", required_argument, NULL, 'c= ' }, {"chroot-fallback", no_argument, NULL, 3= 2 }, + {"pass-fds", required_argument, NULL, 33 = }, { 0 }, }; const char *optstring =3D "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H= :461t:u:T:U:"; @@ -1572,6 +1632,10 @@ void conf(struct ctx *c, int argc, char **argv) case 32: c->chroot_fallback =3D true; break; + case 33: + if (c->mode !=3D MODE_PASTA) + die("--pass-fds is for pasta mode only"); + break; case 'd': c->debug =3D 1; c->quiet =3D 0; diff --git a/conf.h b/conf.h index 19bf9bc..3489f35 100644 --- a/conf.h +++ b/conf.h @@ -7,6 +7,7 @@ #define CONF_H enum passt_modes conf_mode(int argc, char *argv[]); +int conf_pass_fds(int argc, char **argv, int *fds, int max_fds); 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); diff --git a/isolation.c b/isolation.c index 94cbe7f..0632f7f 100644 --- a/isolation.c +++ b/isolation.c @@ -249,32 +249,72 @@ void isolate_initial(void) } /* - * isolate_fds() - Close leaked files, but not --fd, stdin, stdout, stderr + * isolate_fds() - Close leaked files, but not --fd, --pass-fds, standard = streams * @argc: Argument count - * @argv: Command line options, as we need to skip any file given via= --fd + * @argv: Command line options * * Should: - * - close all open files except for standard streams and the one from --= fd + * - close all open files except for standard streams, --fd, and --pass-f= ds * - move the --fd descriptor out of the range 0-2 * * Return: new fd number for descriptor from --fd, or -1 if not specified */ int isolate_fds(int argc, char **argv) { - int fd, close_from =3D STDERR_FILENO + 1; + int prev_fd =3D STDERR_FILENO; // Keep standard streams + int fds[1024 + 1]; + int fds_cnt =3D 0; + int tap_fd; + int rc =3D 0; + + tap_fd =3D conf_tap_fd(argc, argv); + if (tap_fd >=3D 0 && tap_fd < 3) { + /* Move the passed fd to a more convenient location */ + int new_fd =3D fcntl(tap_fd, F_DUPFD, 3); + + if (new_fd < 0) + die_perror("Could not relocate --fd descriptor"); - fd =3D conf_tap_fd(argc, argv); + close(tap_fd); + tap_fd =3D new_fd; + } - 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++; + if (tap_fd >=3D 0) + /* Keep the tap fd */ + fds[fds_cnt++] =3D tap_fd; + + rc =3D conf_pass_fds(argc, argv, fds + fds_cnt, 1024); + if (rc > 0) + /* Keep the pass-fds */ + fds_cnt +=3D rc; + + rc =3D 0; + + while (1) { + int min_fd =3D -1; + + /* Find the next-lowest fd to keep */ + for (int i =3D 0; i < fds_cnt; i++) { + if (fds[i] > prev_fd && (min_fd =3D=3D -1 || fds[i]= < min_fd)) + min_fd =3D fds[i]; + } + + if (min_fd =3D=3D -1) + break; + + if (min_fd > prev_fd + 1) { + /* Close fds between two kept fds */ + if (close_range(prev_fd + 1, min_fd - 1, CLOSE_RANG= E_UNSHARE)) + rc =3D -1; + } + prev_fd =3D min_fd; } + + /* Close all other fds */ + if (close_range(prev_fd + 1, ~0U, CLOSE_RANGE_UNSHARE)) + rc =3D -1; - if (close_range(close_from, ~0U, CLOSE_RANGE_UNSHARE)) { + if (rc) { if (errno =3D=3D ENOSYS || errno =3D=3D EINVAL) { /* This probably means close_range() or the * CLOSE_RANGE_UNSHARE flag is not supported by th= e @@ -288,7 +328,7 @@ int isolate_fds(int argc, char **argv) } } - return fd; + return tap_fd; } /** diff --git a/passt.1 b/passt.1 index 995590a..bcd3aff 100644 --- a/passt.1 +++ b/passt.1 @@ -755,6 +755,11 @@ of local traffic in pasta\fR in the \fBNOTES\fR for mo= re details. Do not create a tap device in the namespace. In this mode, \fIpasta\fR onl= y forwards loopback traffic between namespaces. +.TP +.BR \-\-pass\-fds " " \fIfds\fR +Pass a comma-separated list of file descriptors to the spawned command. +These file descriptors will be kept open. + .SH EXAMPLES .SS \fBpasta -- 2.52.0 --_000_DM3PR11MB871351EF40D8980F4A54F8B7DAC52DM3PR11MB8713namp_ Content-Type: text/html; charset="us-ascii" Content-Transfer-Encoding: quoted-printable
Nitpicks:
  • we use STDERR_FIL= ENO to represent 2, so the target of fcntl should be STDERR_= FILENO + 1 rather than literal 3.
  • I think it would be cle= arer if prev_fd was declared but not initially defined, and then set to STDERR_FILENO just before the loop, so that a comment lik= e /* keep standard streams */ would not be among a bunch = of variable declarations, but would be located near where the variable is f= irst used. 
  • I think it would be cle= arer if min_fd was named next_fd by analogy to prev_= fd.

From: Richard Lawrence <= rlawrence@tamu.edu>
Sent: Saturday, July 18, 2026 11:14 AM
To: passt-dev@passt.top <passt-dev@passt.top>
Cc: jbash@jbash.com <jbash@jbash.com>; Lawrence, Richard E <= ;rlawrence@tamu.edu>; Lawrence, Richard E <rlawrence@tamu.edu>
Subject: [PATCH v2] feat: Add cli option '--pass-fds' for pasta mode= .
 
From: Richard Lawrence <rarensu@tamu.edu>
When pasta mode is used to launch an executable (`pasta [COMMAND]`) and tha= t 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.

The `pass-fds` option accepts a comma-separated list of file
descriptor numbers. `conf_pass_fds()` parses the command line argument,&nbs= p; then `isolate_fds()` skips closing the specified fds by calling
`close_range()` on the gaps between them.

Additionally, the tap fd is safely relocated to the lowest unused fd number= which it at least 3, to avoid accidentally overwriting an existing fd.

Signed-off-by: Richard Lawrence <rlawrence@tamu.edu>
---
 conf.c      | 66 +++++++++++++++++++++++++++= +++++++++++++++++++++++-
 conf.h      |  1 +
 isolation.c | 68 ++++++++++++++++++++++++++++++++++++++++++----------= -
 passt.1     |  5 ++++
 4 files changed, 125 insertions(+), 15 deletions(-)

diff --git a/conf.c b/conf.c
index 0fcba5c..3246735 100644
--- a/conf.c
+++ b/conf.c
@@ -732,7 +732,9 @@ pasta_opts:
            &nb= sp;    "        = ;            &n= bsp;  Don't copy all addresses to namespace\n"
            &nb= sp;    "  --ns-mac-addr ADDR   Set MAC a= ddress on tap interface\n"
            &nb= sp;    "  --no-splice     = ;     Disable inbound socket splicing\n"
-            &n= bsp;  "  --splice-only      &n= bsp; Only enable loopback forwarding\n");
+            &n= bsp;  "  --splice-only      &n= bsp; Only enable loopback forwarding\n"
+            &n= bsp;  "  --pass-fds FDS       = Comma-separated list of fds to pass to\n"
+            &n= bsp;  "         &nbs= p;             = the spawned command\n");
 
         passt_exit(status);
 }
@@ -1183,6 +1185,63 @@ int conf_tap_fd(int argc, char **argv)
         return val;
 }
 
+/**
+ * conf_pass_fds() - Read fds as supplied by --pass-fds command line optio= n
+ * @argc:      Argument count
+ * @argv:      Command line options
+ * @fds:       Array where we store the pars= ed fds
+ * @max_fds:   Maximum size of the array
+ *
+ * Return: number of parsed fds, or -1 if option not specified
+ */
+int conf_pass_fds(int argc, char **argv, int *fds, int max_fds)
+{
+       const struct option opt[] =3D { { &qu= ot;pass-fds", required_argument, NULL, 33 },
+            &n= bsp;            = ;            { 0 }, = };
+       const char *fdsarg =3D NULL;
+       int name, fds_cnt =3D 0;
+       int old_opterr;
+
+       old_opterr =3D opterr;
+       opterr =3D 0;
+       optind =3D 0;
+       do {
+            &n= bsp;  name =3D getopt_long(argc, argv, "-:", opt, NULL);
+            &n= bsp;  if (name =3D=3D 33)
+            &n= bsp;          fdsarg =3D optar= g;
+       } while (name !=3D -1);
+       opterr =3D old_opterr;
+
+       if (!fdsarg)
+            &n= bsp;  return -1;
+
+       while (*fdsarg) {
+            &n= bsp;  unsigned long val;
+            &n= bsp;  char *endptr;
+
+            &n= bsp;  val =3D strtoul(fdsarg, &endptr, 10);
+            &n= bsp;  if (fdsarg =3D=3D endptr)
+            &n= bsp;          die("Invali= d --pass-fds option: %s", fdsarg);
+
+            &n= bsp;  if (val > INT_MAX)
+            &n= bsp;          die("Invali= d file descriptor in --pass-fds: %lu", val);
+
+            &n= bsp;  if (fds_cnt >=3D max_fds)
+            &n= bsp;          die("Too ma= ny file descriptors in --pass-fds");
+
+            &n= bsp;  fds[fds_cnt++] =3D (int)val;
+
+            &n= bsp;  if (*endptr =3D=3D ',')
+            &n= bsp;          fdsarg =3D endpt= r + 1;
+            &n= bsp;  else if (*endptr =3D=3D '\0')
+            &n= bsp;          fdsarg =3D endpt= r;
+            &n= bsp;  else
+            &n= bsp;          die("Invali= d character in --pass-fds option: %s", fdsarg);
+       }
+
+       return fds_cnt;
+}
+
 /**
  * conf_addr() - Configure guest address with -a option
  * @c:         Execution cont= ext
@@ -1331,6 +1390,7 @@ void conf(struct ctx *c, int argc, char **argv)
            &nb= sp;    {"stats", required_argument,  &nb= sp;         NULL,   =         31 },
            &nb= sp;    {"conf-path",   required_argument= ,      NULL,      &n= bsp;    'c' },
            &nb= sp;    {"chroot-fallback", no_argument,  = ;      NULL,      &n= bsp;     32 },
+            &n= bsp;  {"pass-fds",    required_argument, = ;     NULL,       &n= bsp;   33 },
            &nb= sp;    { 0 },
         };
         const char *optstring =3D = "+dqfel:hs:c:F:I:p:P:m:a:n:M:g:i:o:D:S:H:461t:u:T:U:";
@@ -1572,6 +1632,10 @@ void conf(struct ctx *c, int argc, char **argv)
            &nb= sp;    case 32:
            &nb= sp;            c->= ;chroot_fallback =3D true;
            &nb= sp;            break= ;
+            &n= bsp;  case 33:
+            &n= bsp;          if (c->mode != =3D MODE_PASTA)
+            &n= bsp;            = ;      die("--pass-fds is for pasta mode only= ");
+            &n= bsp;          break;
            &nb= sp;    case 'd':
            &nb= sp;            c->= ;debug =3D 1;
            &nb= sp;            c->= ;quiet =3D 0;
diff --git a/conf.h b/conf.h
index 19bf9bc..3489f35 100644
--- a/conf.h
+++ b/conf.h
@@ -7,6 +7,7 @@
 #define CONF_H
 
 enum passt_modes conf_mode(int argc, char *argv[]);
+int conf_pass_fds(int argc, char **argv, int *fds, int max_fds);
 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);
diff --git a/isolation.c b/isolation.c
index 94cbe7f..0632f7f 100644
--- a/isolation.c
+++ b/isolation.c
@@ -249,32 +249,72 @@ void isolate_initial(void)
 }
 
 /*
- * isolate_fds() - Close leaked files, but not --fd, stdin, stdout, stderr=
+ * isolate_fds() - Close leaked files, but not --fd, --pass-fds, standard = streams
  * @argc:      Argument count
- * @argv:      Command line options, as we need t= o skip any file given via --fd
+ * @argv:      Command line options
  *
  * Should:
- *  - close all open files except for standard streams and the one fr= om --fd
+ *  - close all open files except for standard streams, --fd, and --p= ass-fds
  *  - move the --fd descriptor out of the range 0-2
  *
  * Return: new fd number for descriptor from --fd, or -1 if not speci= fied
  */
 int isolate_fds(int argc, char **argv)
 {
-       int fd, close_from =3D STDERR_FILENO = + 1;
+       int prev_fd =3D STDERR_FILENO; // Kee= p standard streams
+       int fds[1024 + 1];
+       int fds_cnt =3D 0;
+       int tap_fd;
+       int rc =3D 0;
+
+       tap_fd =3D conf_tap_fd(argc, argv); +       if (tap_fd >=3D 0 && tap_f= d < 3) {
+            &n= bsp;  /* Move the passed fd to a more convenient location */
+            &n= bsp;  int new_fd =3D fcntl(tap_fd, F_DUPFD, 3);
+            &n= bsp; 
+            &n= bsp;  if (new_fd < 0)
+            &n= bsp;          die_perror("= ;Could not relocate --fd descriptor");
 
-       fd =3D conf_tap_fd(argc, argv);
+            &n= bsp;  close(tap_fd);
+            &n= bsp;  tap_fd =3D new_fd;
+       }
 
-       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++;
+       if (tap_fd >=3D 0)
+            &n= bsp;  /* Keep the tap fd */
+            &n= bsp;  fds[fds_cnt++] =3D tap_fd;
+
+       rc =3D conf_pass_fds(argc, argv, fds = + fds_cnt, 1024);
+       if (rc > 0)
+            &n= bsp;  /* Keep the pass-fds */
+            &n= bsp;  fds_cnt +=3D rc;
+
+       rc =3D 0;
+
+       while (1) {
+            &n= bsp;  int min_fd =3D -1;
+
+            &n= bsp;  /* Find the next-lowest fd to keep */
+            &n= bsp;  for (int i =3D 0; i < fds_cnt; i++) {
+            &n= bsp;          if (fds[i] > = prev_fd && (min_fd =3D=3D -1 || fds[i] < min_fd))
+            &n= bsp;            = ;      min_fd =3D fds[i];
+            &n= bsp;  }
+
+            &n= bsp;  if (min_fd =3D=3D -1)
+            &n= bsp;          break;
+
+            &n= bsp;  if (min_fd > prev_fd + 1) {
+            &n= bsp;          /* Close fds bet= ween two kept fds */
+            &n= bsp;          if (close_range(= prev_fd + 1, min_fd - 1, CLOSE_RANGE_UNSHARE))
+            &n= bsp;            = ;      rc =3D -1;
+            &n= bsp;  }
+            &n= bsp;  prev_fd =3D min_fd;
         }
+      
+       /* Close all other fds */
+       if (close_range(prev_fd + 1, ~0U, CLO= SE_RANGE_UNSHARE))
+            &n= bsp;  rc =3D -1;
 
-       if (close_range(close_from, ~0U, CLOS= E_RANGE_UNSHARE)) {
+       if (rc) {
            &nb= sp;    if (errno =3D=3D ENOSYS || errno =3D=3D EINVAL) {
            &nb= sp;            /* Th= is probably means close_range() or the
            &nb= sp;            = * CLOSE_RANGE_UNSHARE flag is not supported by the
@@ -288,7 +328,7 @@ int isolate_fds(int argc, char **argv)
            &nb= sp;    }
         }
 
-       return fd;
+       return tap_fd;
 }
 
 /**
diff --git a/passt.1 b/passt.1
index 995590a..bcd3aff 100644
--- a/passt.1
+++ b/passt.1
@@ -755,6 +755,11 @@ of local traffic in pasta\fR in the \fBNOTES\fR for mo= re details.
 Do not create a tap device in the namespace. In this mode, \fIpasta\f= R only
 forwards loopback traffic between namespaces.
 
+.TP
+.BR \-\-pass\-fds " " \fIfds\fR
+Pass a comma-separated list of file descriptors to the spawned command. +These file descriptors will be kept open.
+
 .SH EXAMPLES
 
 .SS \fBpasta
--
2.52.0

--_000_DM3PR11MB871351EF40D8980F4A54F8B7DAC52DM3PR11MB8713namp_--