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=Y/kNYxSE; dkim-atps=neutral Received: from mx0a-00178102.pphosted.com (mx0a-00178102.pphosted.com [148.163.135.245]) by passt.top (Postfix) with ESMTPS id 72B815A0262 for ; Sun, 19 Jul 2026 21:14:10 +0200 (CEST) Received: from pps.filterd (m0231228.ppops.net [127.0.0.1]) by mx0b-00178102.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66JHpX473902055; Sun, 19 Jul 2026 14:14:04 -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=yZ6aiIJPkb17LvCM3fpPcZZ1L OaSWKR8BoO8zylqsL8=; b=Y/kNYxSEyLgozVgQUnavSndHGhNJcNtIDpjMPujeF Rbbpgcm63HVFgs+MISfmDvB/XJYjoiGe5ra+IBnI7CZfPuY1frTMk5tBVqWCFzZF 4kCkr+htU/x5MeLRjhepM6urQTYzcT88906lIV6MCZ2aUfAUfO/bUY44v5fP2Eer CbzCCRYiCtZU0TUhSBwY0xOMnv4Enh5PMEGUCrfsuKiNT7VVKv+u8gOT364JGDi/ RNjqEaV7Y4Gcbum7S50ytqyMbLQVtiFmkZdwvskdK5X6r8v6Pb3OhT6ZXbBgzZoU MI6ZuL1O0EkocxyTjMFVxzTPEDpcl/mVvT/edebB6ueYQ== Received: from sn4pr2101cu001.outbound.protection.outlook.com (mail-southcentralusazon11012048.outbound.protection.outlook.com [40.93.195.48]) by mx0b-00178102.pphosted.com (PPS) with ESMTPS id 4fg8mmxwr0-1 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Sun, 19 Jul 2026 14:14:04 -0500 (CDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=D9QDPoOmSa+3r7uUkxhQKMAOV8OTnhXqBwtYeknLA+LV1X19rW9rol+UfD+qAmxJiZaRGYLx1kX0b7QNg46SM0ql+vLUFkErJDUIFMMxzId8OsdAKhq+qHq8S01jeAQjF9K42/uX4umAQNNX+MBA5egDduufIGhJ2ghaj/D9t28VOPWfryHAj9nU/40KS91SgY9EtoNWUw3lvf/+9sAarypmhSj7uXu4x9MJ5awRpqPamOdYuK560l8ylm9jNWK5qYZQEYbI2e6WYqvZfhb/08U8f0bUSfOyMgNsx0xnVg/QEWRxpATozBJ5x8OWCRXTQNRoLxYm/ncIjZLzI/6kpg== 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=yZ6aiIJPkb17LvCM3fpPcZZ1LOaSWKR8BoO8zylqsL8=; b=gdgDC+ZSijsukAh0NWPgkAQjyrb0+TFWvpZqOvoi2PNvY8/Q/QgcjdTHTz37Je2i9+iLN0dJQ/ct3zYasoAy79D1481qWzjJlTuR2dd/rdC8b/kYbOQYOUW4INBAlEPsP/vu+1NxWTzQbk29hvtFMmSlwQ1TMcEu5abzedOEOIVhedssM0iQj3STFfhaUUtBc6mGegxr2g7pz52/CRkkpzVsw39Yck3DE+bZYQ2l2KjMaT2gg979/GMnJWaIQS0xGNWU9snMDtn1rPGfe2NSGXVdy9OO+Zie+ITlAiYjhP20W7NVl4q2jC3kl/GneCqvByQr/GK3j+nSNfdtTZByNQ== 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 CH3PR11MB8210.namprd11.prod.outlook.com (2603:10b6:610:163::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.223.16; Sun, 19 Jul 2026 19:14:00 +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.015; Sun, 19 Jul 2026 19:14:00 +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: AQHdFtCKDa22cntO2kKEI5FH0THnEbZze44XgAG5HFk= Date: Sun, 19 Jul 2026 19:14:00 +0000 Message-ID: References: <20260718161429.173494-1-rlawrence@tamu.edu> In-Reply-To: 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_|CH3PR11MB8210:EE_ x-ms-office365-filtering-correlation-id: 5244015d-4c99-45fd-cd03-08dee5c9e3a6 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0;ARA:13230040|1800799024|366016|23010399003|786006|376014|38070700021|18002099003|22082099003|3023799007|8096899003|11063799006|56012099006|4143699003|10067099003|6133799003; x-microsoft-antispam-message-info: 6rww7JFfR+qxXmz+WsUb/joRDHgiGMRPW9NIDlL4ddMFqUAtu2aFqElzAm0mkZxkeRvQIEap+NvW9v+q9x5WvAXw//164WnO4+t9FKf++EU5aJ4neY1Qn1KaYgIhDz1ShgdtiUu158wuidOpnMlltYJE3imTKYYP+6QdC4mI00POdwjqjawcP0TsISZ6N5zTfbQyjqZx5k4UZagmM0NY9Xhu1RhaszXuh8iQabXYlBM+f436/JKjQCAKXF0nViDGMmmBtmP7+DSMVZA4kJCM2whbcjhezHNnVab8zyr8O1fONoKkcJPk16ZVyGkJ39fRxqHhxr3tGkZUi0DedLURRIozxvUg7SvfvG0FUh0W/3WnsbiwThCscHDczzCmGkNUN1MGUHp4n7b+SfNKV13c0KXXYjxItAKE7jfJAiMOnzHb5B5OuxtFI8iwuRO0KVXGXdCJ/46lXOBqdLV0vnlZHa7WVpMmourMMkx8arNcbig6JZwmAbe/TtTjHNrfi+YPQlBuFgI9tjqG98gwtjXpJNSQLYbf5PH5S0o3r5MLeHkUB8UsAUsJt0p8UFs6OAaUh5t+Xmey55zXw2M2jqjSudKuR0mXMhAIAlkjj5csbUsajn/i8cJfi89AjK9Qdzqs/saXfBEf4safQcO/h9jHfmzOuqtGbDSiq8Y0ju5Bwp/SKhFU/U2iinWaTglw+H+PnSXHUqcIbL7AMIdJQN4qnvpngnYjz+qeJCB+svgcHRs= 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)(1800799024)(366016)(23010399003)(786006)(376014)(38070700021)(18002099003)(22082099003)(3023799007)(8096899003)(11063799006)(56012099006)(4143699003)(10067099003)(6133799003);DIR:OUT;SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?Fs39MyatjC81GpXNmX4uU2W/BfY9Ka17+pelkCkAKuPwOeHINlTDCzz/K4eq?= =?us-ascii?Q?DbJI4dDBEntuEDYwaY7rKAbtAiYu/z+S4qRaY3Y4rDv3KUf/5+kvDEOXZczc?= =?us-ascii?Q?s6lOgRiXYxt2JVEBo2NdpFLb1+sT0tCpZexiaVHXiBGu3WRT4YzEokAxQKb3?= =?us-ascii?Q?uc0wXW8gwgMwmpxDB5A7POH4FIjnnPB3WOf5K5jQHpwcFYE8ODk4LB3Eiu2E?= =?us-ascii?Q?+7zyNk6D1hDABisQtEbWFkuJ3LJ9bT8mSG7DI4wcrvELwY8OfWRWKHVd9Rlk?= =?us-ascii?Q?csPigZF/+98zol7QGxr0ZVIxciTSyDiT7QNW54rx4fzlHJIxD9X70BCY+/uU?= =?us-ascii?Q?m/JxdG6xj6uCBifqOj2HlzmBlKWCtRPKSb1xp6B7llTiobUEQBgQwLa2pwzR?= =?us-ascii?Q?48lwemL5Ux+bCRGrI1XgWV2d9iU21n7bTL1j4EXUeRHt4x1VQoA3vsvnT/Dg?= =?us-ascii?Q?XfsaY/zsE9k/ah6ZRWC5bgOPEVwFHalGwsX0f8vqlzbztqBvZgVK6QeMhw/G?= =?us-ascii?Q?Qwzoh4iP48y+PS+I5p+k/PHnoeMJB1dORsJZRRzPusQerjSKM35rSWgOb4UB?= =?us-ascii?Q?PwHDPTkdNZuOLZqSRNbzznTCcCa8e7CUNxwgrilxMm6On5p8pCRwJrjfoi6g?= =?us-ascii?Q?w0IagPUu+Fwq/RoV4nhjz+0oxr68bi24KJhw6JHMhdK5BiaX+/oxOUOPfoKq?= =?us-ascii?Q?VuARoWA4krvhqMrKW98ZmeeB/3doh7L2TQiJXOZxS2f0UDY68HxNzOLlykaq?= =?us-ascii?Q?FiLx/Dyg6wY7mSDkVnEjtUdcfmj1eFO6KIEtVHYCCzjrcWC07Mbp7a7RXwnv?= =?us-ascii?Q?UKJPHf5TCDIQeIq6FRlvtFzcCHAaD3cDYVVLXYdwLpNMbhuhXYfP6pIasx3v?= =?us-ascii?Q?EAJYeAzbXlqRP7W2raUZCJKL50tTvmVvLTMASBeG6cPi+HpOy1cy5aW1RiRl?= =?us-ascii?Q?bLfVFrZnNo7KAz5nYUSz4gj6i0T1hal+ReFTSQYl0Ek4VaoXHWWgTLZSOvnV?= =?us-ascii?Q?gXLxq7dtzZqONDpAZGYp1UEdq89w/SHqFq9VgObE9u6mQYwghzNrL03AKlbW?= =?us-ascii?Q?yCyDaucPajc6GpR+vG/6X/F/Fo37bT20Q/9XSpW5EqRUKSPGJ7V40H+m7Q8L?= =?us-ascii?Q?nrJ/gjRUDfKhEbSg6Ily4uw+aZP94xhcSJJlmfm7J7IHdw2M19StBqqsFYz9?= =?us-ascii?Q?2GtCj6+sye9G1WDAlyD7Fb/njQtwoztX1dYlosAolPMOb4YAn6Zy9uwGBhCt?= =?us-ascii?Q?S7opXRmuRsShRC/B5Ig/l5YIZ86QxU7ly8Z3AX/Krt2fMrLNwiCD5IR+EilO?= =?us-ascii?Q?I+WZ1CS/pzW1DOHm0DfR5uF6rofZansCt4Zv0fehce7TNfkdXgc+Y/wtpHK/?= =?us-ascii?Q?sFUmg3Fx7IbnOJY0CqJMyXTS0rwCUAKC9IGNCZ68gqGPiHabctZKa1NZnoKN?= =?us-ascii?Q?/MSkfIgOGlx6zC570aLtqleLGS2xxQ0YYAOaQiym9My+5pQb8yYc1mtAEWmy?= =?us-ascii?Q?h6RZ1SxRJ7Bs9PvbGSBfDfuZI3c0TykEiHgkh+HxxKpxzTrnsG3wExqrJq8c?= =?us-ascii?Q?QNk0qyh2SDGoFt8j/q8qK7hNkU2Di+iZKrEP4/ggGFW5RcyXThiAjFoO23qa?= =?us-ascii?Q?yFircD6/1ErZYfXoi9YurLhXs2Sz+6icchkTsWUSFTkNt06CIicVk1pwKxRc?= =?us-ascii?Q?Mu7XhpmMr7DEW/BF0+Y5RP42kwSR74Qu222sUQ5t0peCav7W?= Content-Type: multipart/alternative; boundary="_000_DM3PR11MB87131A9BAF123969BC0744F0DAC42DM3PR11MB8713namp_" MIME-Version: 1.0 X-Exchange-RoutingPolicyChecked: hzL+2xbOl86JLZbjg25cFdL4tdXy2bdIdUuQmeb9bQMM7QirvVArbK2R771+M93dItuWMNeG9ruZFAdy34pubrH+Z0a4X/NimBlO8opq07Ub16fMrNpoF2OMz8lux8K1J4E6XBiEcCTAz0M9S4tR0W1cZQKz0hiLRyQkA8CocT5xhVYiSt0ZYT60mt2cXHoX4uaoXLcsGf0JnC2d3tJMpXCP4HhQOB9rMbd6jGLJs/vN2YBxP7IdmRNKZcOtcngrhzF6YHWwNTpo1b6NcpFaOCIknjfFIOfF70omHuUcOPNLzywOodg9iuSRily0EhSSayGrx5SIZZY7lnuI1kw72A== 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: 5244015d-4c99-45fd-cd03-08dee5c9e3a6 X-MS-Exchange-CrossTenant-originalarrivaltime: 19 Jul 2026 19:14:00.3184 (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: yXrGsIQGggMBYz2vdqbDIKMsWNTL4uUr6NI1spaCsvW2JNKSvZ0vjyCo8zdQfBQzgskzNNk1xy2urC2SYVqrKw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CH3PR11MB8210 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE5MDIxMyBTYWx0ZWRfX/3qgANT19zWW uI2MTDlqiHUUMeTfDosHQm6u8zmepr0Sas+aeUnPo8lT03/EFC592zQO25jBeXVNnWQmB5fCr2G +60yBRlmd2+UVFemutTuYNe+J+hxbro= X-Proofpoint-ORIG-GUID: Ie3vYN2sLzpuBfVNTe9IRMO-sxer9K0d X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE5MDIxMyBTYWx0ZWRfX+tng7saplI7R YmOEZcqShWoXqJCU/4vIFoSnNGz4WiLly+dupW/jdDjGG3rNP9oAac94CS0sBzn2lWI2+jmV+NT GANRjTMEjGSq6XNX4LQpZr9gCCUxF6TIFXTSFLveZjtUihGj1PFXzpIw5FuG4ELVUoH5URtH6/r pjZk0Q218t4oW2tYhzV3EeQYcKFqtvz/niJjM5V311ScPD1U/WkVdpI1t42YRbijkN027OtHIoi 4058erDsIzTbxS4rK4HfYgdOOqnWgW6pzjqxciCjk+K6aW/Dr5YFMYasBbW2GqteguzYypopgN5 6WMSUoacl1RjY7ZLpSqYgNq7+9z47vM/QsvpoX90s7Yg3i3kBQ3YiyFM2c1wYuEHSm38U4H+JMJ n+vz4SqWmBiciHpXxz8BCWO1MxKusw8G/nIQS1IDfo6EtE7XeMd8wzLJ6c+eRrPjJazaEpcmyUd g6IW1AzkbiUBFY/dkWA== X-Proofpoint-GUID: Ie3vYN2sLzpuBfVNTe9IRMO-sxer9K0d X-Authority-Analysis: v=2.4 cv=RcCgzVtv c=1 sm=1 tr=0 ts=6a5d21fc cx=c_pps a=DAzpKO+L5eHZuocb3FCBhQ==: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=q_m3wMv7_deI2xT0_EXV:22 a=btVHSsReAAAA:8 a=svcJDqgjN0kaiDdzVVAA:9 a=CjuIK1q_8ugA:10 a=uplRppl4hImwDixd4lcA:9 a=dJk0j08VI8IkeNIi: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-19_06,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: N6TLWQRLUBD7NTXQZS53VZFHLY4VZUPZ X-Message-ID-Hash: N6TLWQRLUBD7NTXQZS53VZFHLY4VZUPZ X-Mailman-Approved-At: Sun, 19 Jul 2026 22:35:56 +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_DM3PR11MB87131A9BAF123969BC0744F0DAC42DM3PR11MB8713namp_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Nitpicks: * Literal 1024 used as the max pass-fds is magic. It should either be dynamic= , or defined as a preprocessor directive. * It is at least computationally awkward and possibly even unsafe to parse fd= and pass-fds independently from the rest of the args. If the arg structure= is ever extended to support an unordered mix of keyword and positional arg= uments, then these independent implementations become a maintenance burden. * In conf_pass_fds(), the die statement only includes the particular substrin= g that triggered the failure, which could result in a misleading or unhelpf= ul error message. ________________________________ From: Lawrence, Richard E Sent: Saturday, July 18, 2026 11:52 AM To: passt-dev@passt.top Cc: jbash@jbash.com Subject: Re: [PATCH v2] feat: Add cli option '--pass-fds' for pasta mode. 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_DM3PR11MB87131A9BAF123969BC0744F0DAC42DM3PR11MB8713namp_ Content-Type: text/html; charset="us-ascii" Content-Transfer-Encoding: quoted-printable
Nitpicks:
  • Literal 1024 used as th= e max pass-fds is magic. It should either be dynamic, or defined as a prepr= ocessor directive.
  • It is at least computat= ionally awkward and possibly even unsafe to parse fd and pass-fds independently from the = rest of the args. If the arg structure is ever extended to support an unord= ered mix of keyword and positional arguments, then these independent implem= entations become a maintenance burden. 
  • In conf_pass_fds(= ), the die statement only includes the particular substring that tri= ggered the failure, which could result in a misleading or unhelpful error m= essage.

From: Lawrence, Richard E &= lt;rlawrence@tamu.edu>
Sent: Saturday, July 18, 2026 11:52 AM
To: passt-dev@passt.top <passt-dev@passt.top>
Cc: jbash@jbash.com <jbash@jbash.com>
Subject: Re: [PATCH v2] feat: Add cli option '--pass-fds' for pasta = mode.
 
Nitpicks:
  • we use STDERR_F= ILENO to represent 2, so the target of fcntl should be STDERR_= FILENO + 1 rather than literal 3.
  • I think it would be c= learer 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 c= learer if min_fd was named next_fd by analogy to prev_= fd.

From: Richard Lawrence &l= t;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><= br>
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_DM3PR11MB87131A9BAF123969BC0744F0DAC42DM3PR11MB8713namp_--