Dear Genode community,
I've joined Denis Huber's project on developing a real-time checkpoint/restore component for Genode.
( https://sourceforge.net/p/genode/mailman/message/35211808/ )
In order to improve our process of memory checkpointing we would like to distinguish reading memory accesses from writing memory accesses. While we do not care about reading, we are highly interested in detecting the occurrence of writing accesses.
At the moment our way to detect the access to a specific memory region is to detach the corresponding dataspace and handle the pagefault which is produced when the component tries to access the dataspace.
We are aware that it is possible to determine whether a writing or a reading access triggered a pagefault but we don't want to increase the amount of pagefaults by detaching dataspaces after every time they were only read. It would be cool if writing would cause an error which is is not produced by reading.
As a solution to this we thought of adding the possibility to make a RAM dataspace read-only, something which is similar to „mprotect“ in Linux. With this feature we would not need to detach dataspaces but writing accesses would still produce faults which we could track in order to detect them.
Is such an alteration of RAM dataspaces possible? If yes, how would you do it?
What kind of fault would be triggered if someone tries to write to a read-only memory?
Are we able to get information about such a fault in Genode?
We are using Genode on top of the Fiasco.OC.
Thanks in advance!
Kind Regards,
David
Hi David,
As a solution to this we thought of adding the possibility to make a RAM dataspace read-only, something which is similar to „mprotect“ in Linux. With this feature we would not need to detach dataspaces but writing accesses would still produce faults which we could track in order to detect them.
Is such an alteration of RAM dataspaces possible? If yes, how would you do it?
What kind of fault would be triggered if someone tries to write to a read-only memory?
Are we able to get information about such a fault in Genode?
there exists actually an issue for this topic:
https://github.com/genodelabs/genode/issues/1633
Right now, the information of whether a dataspace is read-only (RO) or read-writable (RW) is stored solely in core's 'Dataspace_component' objects and it is fixed at the creation time the dataspace. I am actually planning to rework Genode's page-fault handling code this year and as part of this work, I plan to address the problem. My current ideas look roughly as follows:
Dataspace aliasing
At some point, we had the idea to allow the creation of a ROM-dataspace from a given RAM-dataspace. So the owner of a RAM dataspace could create a read-only alias for the same dataspace. However, we eventually dismissed this idea for two reasons. First, it complicates core's 'Dataspace_component' implementation quite substantially. In fact, we needed to have two component objects for each dataspace in order to differentiate the capabilities. This raises the question when to create the RO-alias, at dataspace-creation time or only when needed? The former imposes overhead on each dataspace, the latter results in more complex code and raises meta-data accounting questions. Second, by introducing aliases for dataspaces, we would lose the nice property of Genode (from an security assessment perspective) that each physical memory page is present in not more than one dataspace at any time.
RO attach
In addition to the read/write permission as stored in the dataspace, we may add the same property to 'Region' objects, which are created whenever a dataspace is attached via 'Region_map::attach'. By specifying that a dataspace should be attached read-only, the resulting mapping will always be read-only regardless of whether the underlying dataspace is writable. As far as I see, the implementation wouldn't be too hard. When resolving page faults, the pager would logically-AND the r/w conditions of the traversed region maps (they may be nested so the downgrade from RW to RO could happen at any level) with the property of the dataspace.
I think that this mechanism would fit your checkpointing scenario quite well because your custom runtime virtualizes the region-map access already, doesn't it? So whenever the monitored component performs a RW 'attach' your RM service could silently perform a RO attach and thereby observe an RM fault as soon as the monitored component attempts to write. It would then respond to the fault by detaching the RO region, followed by temporarily attaching the dataspace as writable.
Permanent downgrading (sealing)
We observed that in the most common use case for turning a RAM dataspace into a ROM dataspace, there is no need to write to the dataspace at arbitrary times. In fact, usually, such a RAM dataspace is populated with data only once (e.g., when loading a shared library from a file system, one would load the code segement into a RAM dataspace, but after loading, this content would never change until the destruction of the dataspace). This observation led to the idea to add a 'Ram_session::seal' operation that permanently downgrades a RAM dataspace to become RO. In contrast to the RAM/ROM aliases discussed above, this approach would not come at the expense of any overhead in terms on meta data or complexity. The only slightly tricky part is the transition from RW to RO where all existing RW mappings must flushed.
You mentioned that you'd like to differentiate read from write faults. This may actually be not needed if you follow the RO-attach approach because your runtime knows where all dataspaces are attached and whether it attached the dataspaces as read-only or writable. That said, I would not object to add this information to the 'Region_map::State' either, which could be requested whenever a RM fault is observed.
Cheers Norman
Hi Norman,
thank you for you answer!
The RO attach sounds indeed like a mechanism that would fit our scenario.
May i ask you some more questions on this idea?
RO attach
In addition to the read/write permission as stored in the dataspace, we may add the same property to 'Region' objects, which are created whenever a dataspace is attached via 'Region_map::attach'.
Does the '_write' flag in 'Rm_region' already specify whether the 'Region' is writable or not? May i use it to determine if the corresponding Mapping should be writable or do i need to implement another flag?
By specifying that a dataspace should be attached read-only, the resulting mapping will always be read-only regardless of whether the underlying dataspace is writable. As far as I see, the implementation wouldn't be too hard. When resolving page faults, the pager would logically-AND the r/w conditions of the traversed region maps (they may be nested so the downgrade from RW to RO could happen at any level) with the property of the dataspace.
As far as i can follow your description i would have to do the following two things:
1. Provide an alternative implementation for 'Region_map_component::attach' which allows to create a read-only 'Region' (corresponding flag set)
2. Modify the method 'Rm_client::pager' in order to take the flag of the region into account when creating mappings. (At the moment only the property of the dataspace is considered)
Is that everything or did i miss something?
Kind Regards, David
Hi David,
Does the '_write' flag in 'Rm_region' already specify whether the 'Region' is writable or not? May i use it to determine if the corresponding Mapping should be writable or do i need to implement another flag?
indeed, the flag is unused right now but it was apparently designated for this purpose. Please go ahead with using it.
By specifying that a dataspace should be attached read-only, the resulting mapping will always be read-only regardless of whether the underlying dataspace is writable. As far as I see, the implementation wouldn't be too hard. When resolving page faults, the pager would logically-AND the r/w conditions of the traversed region maps (they may be nested so the downgrade from RW to RO could happen at any level) with the property of the dataspace.
As far as i can follow your description i would have to do the following two things:
- Provide an alternative implementation for
'Region_map_component::attach' which allows to create a read-only 'Region' (corresponding flag set)
- Modify the method 'Rm_client::pager' in order to take the flag of the
region into account when creating mappings. (At the moment only the property of the dataspace is considered)
Is that everything or did i miss something?
As far as I can see, that's all. Good luck!
Cheers Norman