From nobody Sat Nov 15 16:45:34 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1750437742; cv=none; d=zohomail.com; s=zohoarc; b=PqmHA1R+2JAvRDmkpOIeMWf8DJEi9nano1E4I2LDh0JDozMENPEkqsAFkEwgg+ypMH+fBHPAjEVjTqL/fXf6y+XiJOJgaXQuGbu1vK88I3n2syyE2vwMTa5iqW344qb1UGw7Rpn1ksoKLHJ+YIorsMYksIkJ/Oj0WvnTyzSpNAo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1750437742; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=P0ToVja12+bxntDkM4pyBWaD2U29e/LsVsIKe4q19RE=; b=RCG8jUVimC12wKv2Jrpb+f508iFkovoUY1yWW2R4KYvEzAY4xqwpYWuiQwQpaxuNNYyl+/tAtTWNLFAaEf8jUc50vhDq39XdTjvueyd+vjVMMOhGM/axIjFm+dCv70T3Y/IuLRrHA0Ao0Zpyi++l+3TfL2SfMTiFj4bLbM33ItI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1750437742246894.5927296966588; Fri, 20 Jun 2025 09:42:22 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uSenv-0006XV-Dd; Fri, 20 Jun 2025 12:41:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uSens-0006X0-1u for qemu-devel@nongnu.org; Fri, 20 Jun 2025 12:41:24 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uSenq-0000Fk-8m for qemu-devel@nongnu.org; Fri, 20 Jun 2025 12:41:23 -0400 Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-317-FcvnPXdGOOGAuNXNCHySiw-1; Fri, 20 Jun 2025 12:41:20 -0400 Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-ad89a3bcc62so152222066b.0 for ; Fri, 20 Jun 2025 09:41:19 -0700 (PDT) Received: from [192.168.122.1] ([151.62.200.93]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-ae053edc0e9sm188735466b.54.2025.06.20.09.41.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 20 Jun 2025 09:41:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750437681; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=P0ToVja12+bxntDkM4pyBWaD2U29e/LsVsIKe4q19RE=; b=TLwcbRKwPLCxW5XzClk8SD2EniBPmlnZN76esUnQ/dQQRvoW/Wi0tQXZEAMZ7mHkd84baH Pkuf0g4y9v/uNg9ZWHBYsTbXFOehD2Ofho5FSTxuXfs065pIi0BFmvEXS3i8Xll14/LIzp XWPiFJB7mHX7Us4/bNlgq5l+Z0/O+6E= X-MC-Unique: FcvnPXdGOOGAuNXNCHySiw-1 X-Mimecast-MFC-AGG-ID: FcvnPXdGOOGAuNXNCHySiw_1750437679 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1750437678; x=1751042478; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=P0ToVja12+bxntDkM4pyBWaD2U29e/LsVsIKe4q19RE=; b=BibAXEYiKdY691R+7ysMNrO7B/onuaZTxHy7TY5HjC5js5KKyEQ/ppBRyBR1siRw2A sGmSrWBFX79M/0pVFpFGFEMgOjCpFuwfoftzSZaD2RJ/MjeGHjRvig7zhNT309FR/UWY VAnqrrXEAZV0y/W5z4G2QpUM6l7VVRQTaoKMPe1zCbVZGZQbBNC9X8CW75V/9Hz01zvo 8kOLZm1lHffWlXLcUNs8tre7CoxneW6Glo1TyGE3pvfELEcDYpfdirY9FICLKnjewco4 DPA7tUZbqizlAYMd1SPqGU1TctjVzsYUiPJN5kxKHXKJEnp46DoWeeY7BkqbA7YqxwYq ulAA== X-Gm-Message-State: AOJu0Yx/907Qaxakyz+P/SVkcHN0iWFAFsiVmbp9QER+46CmhByatHYK IiVamvfCX8XdzymGhEWS2Bcb3OVPptYbmKkzO0F7DYSHMfbRy1BhoynEtpb0hqithCs7c+U+sax Cthm7QOyDFjms9scbJrufp/PGTATwMrG5H+JaD21aw3p5qZc6FxZ9aWGv8PlKcbn4XUYSOdk6sz R2hEBTngTcTINMG9yyiMpvkxvEhTjHlrp/98AWrU1r X-Gm-Gg: ASbGncvSZjyTim+4afLgT3akYaoefs8KxrGQtkiHuLZw0FOdYSBqcHyQoiCVJFPXBDY O2GT649NzVNtv92TgZ0zVI+I+6VRm2dzi4QVUr3lU0t9Wtj04NY2EkqewFXw952Vylxv0Q7H085 gilbBaOnNvquyUal5NtE2eJJZD9BVfDd2mW4PifdENA6WenXsImEZxu5ogXYYSpbQkHEuPbxeeA WiD2rSmg0+jbCa9nTdfswGy1pLVgFGrs8EECrnZBexxaPUQtfU+MjM4ZtNdVqjMsWPJExBs5dlJ EtuWA6+tp+WzTKDv5aSmidyTmA== X-Received: by 2002:a17:907:1b29:b0:ad8:9257:573d with SMTP id a640c23a62f3a-ae057a0826bmr325331966b.24.1750437677760; Fri, 20 Jun 2025 09:41:17 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHv+cX4lo9hxNHLN1cuQI0Zi/KLvmxBBXi8M9d4HlRrjFgbQzjm+HjNmik3eZjbeofkiwtadA== X-Received: by 2002:a17:907:1b29:b0:ad8:9257:573d with SMTP id a640c23a62f3a-ae057a0826bmr325329366b.24.1750437677218; Fri, 20 Jun 2025 09:41:17 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Zhao Liu Subject: [PULL 08/24] rust: qom: make ParentInit lifetime-invariant Date: Fri, 20 Jun 2025 18:40:36 +0200 Message-ID: <20250620164053.579416-9-pbonzini@redhat.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250620164053.579416-1-pbonzini@redhat.com> References: <20250620164053.579416-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -39 X-Spam_score: -4.0 X-Spam_bar: ---- X-Spam_report: (-4.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-1.897, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1750437743359116600 Content-Type: text/plain; charset="utf-8" This is the trick that allows the parent-field initializer to be used only for the object that it's meant to be initialized. This way, the owner of a MemoryRegion must be the object that embeds it. More information is in the comments; it's best explained with a simplified example. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 89 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index ef966e570ca..04d102591dc 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -95,6 +95,7 @@ use std::{ ffi::{c_void, CStr}, fmt, + marker::PhantomData, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, @@ -208,12 +209,92 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(= ), fmt::Error> { =20 /// This struct knows that the superclasses of the object have already been /// initialized. -pub struct ParentInit<'a, T>(&'a mut MaybeUninit); +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](htt= ps://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect= to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative= when +/// operating on types of type `ParentInit` and to extend their lifetimes.= In +/// particular, it ensures that the `ParentInit` cannot be made to outlive= the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can = help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init =3D Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of = the +/// closure: +/// +/// ```ignore +/// let x =3D 42; +/// let escape =3D Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" s= tay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identi= ty" +/// println!("{}", token1 =3D=3D token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 =3D=3D token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit, + PhantomData &'init ()>, +); =20 -impl<'a, T> ParentInit<'a, T> { +impl<'init, T> ParentInit<'init, T> { #[inline] - pub fn with(obj: &'a mut MaybeUninit, f: impl FnOnce(ParentInit<'a,= T>)) { - let parent_init =3D ParentInit(obj); + pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(Pa= rentInit<'id, T>)) { + let parent_init =3D ParentInit(obj, PhantomData); f(parent_init) } } --=20 2.49.0