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