dynrec vs. secure platforms - opinions wanted

Developer's Forum, for discussion of bugs, code, and other developmental aspects of DOSBox.

dynrec vs. secure platforms - opinions wanted

Postby jmarsh » 2019-4-25 @ 08:07

As more operating systems adopt stricter security policies, there is a trend to stamp out programs using memory mapped as writable+executable. This is a problem for DOSBox's dynamic recompiler, as it currently just allocates a large chunk of cache and marks it read+write+executable. For now it works but probably not for much longer - SELinux is starting to see widespread use, macOS is pushing their hardened runtime, and other platforms just flat-out don't support it (no mprotect or similar mechanism) requiring other methods. This thread is to discuss at least two possible alternatives and get opinions from other devs about what the best fix would be, rather than a bunch of completely different fixes being implemented in different builds.

(Note that DOSBox has two distinct dynamic recompilers; one is generic, the other is x86-specific, they both allocate their cache the same way so for the sake of this discussion just pretend they're one and the same.)

What the current code does
Windows: Uses VirtualAlloc to allocate the code cache as read+write+executable. That's it.
Other platforms: Uses malloc to allocate the code cache and then (if the platform implements mprotect) uses mprotect to set that memory to read+write+executable. This is actually wrong (undefined behaviour); mprotect is only meant to be used on memory allocated using mmap, so that's at least one thing that needs fixing.

Approach 1: Switch between writable/executable
This method's pretty simple. The initial allocation is done with no permission (PAGE_NOACCESS / PROT_NONE) and when code is going to be placed into the cache it gets "locked" (marked as read+writable) then "unlocked" when writing is finished (marked as read+executable). This is pretty simple to implement in cache_openblock/cache_closeblock, only takes a few lines of code and doesn't require any changes to the architecture-specific dynrec source files. It does require some lines to be shuffled so that cache_block_closing() can be called while the memory is still writable (in case data/instruction cache flushing/invalidation is required) but that's trivial.
Pros: Simple to implement.
Cons: Relies on mprotect/VirtualProtect or other function to change memory permission, which not all platforms have. SELinux might still return EACCES when using mprotect to switch memory to executable. Small processing overhead every time a new CodeBlock is written as pages have to have their access modes switched back and forth.

Approach 2: Map the same memory at different addresses using different permission
This is more complicated. Basically you use mmap to map a file descriptor using read+write permission (which gives an "out" pointer), then mmap the same file descriptor using read+executable permission (which gives an "exec" pointer). Code is stored using the "out" pointer but the "exec" pointer is used for relocation calculations etc. since it is the address that will actually be executed. Most of this functionality would be hidden inside the cache_add* functions, if we wanted to get fancy it would be possible to turn cache.pos into a class with overloaded operators so referencing it would yield the exec pointer while dereferencing it would use the out pointer.
The main issue with this approach is that mmap (or MapViewOfFile on windows) requires an actual file (descriptor) for multiple mappings of the same physical memory. There are ways in linux to avoid using the actual filesystem (memfd_create,shm_open) but these are non-portable and may not always be supported depending on kernel configurations, so can't be relied on.
Pros: Practically no overhead. The recent NX (Switch) port uses this method. Ulrich Drepper suggests this method when working with SELinux (maybe this should be a con...)
Cons: A bit fiddly to setup and may be non-portable. Likely requires a physical file to be present while DOSBox is running, although it could be unlinked as soon as it is mapped (meaning it "should" be cleaned up as soon as DOSBox exits). May require some small changes to the existing risc_*.h source files.

So those are the options I see so far. I would like to hear from other devs (particular those running their own ports on non-mainstream OSes) what they think the best path to take is, and hopefully find a solution before everybody's GoG-bought DOSBox games start to become unrunnable.
jmarsh
Member
 
Posts: 132
Joined: 2014-1-04 @ 09:17

Re: dynrec vs. secure platforms - opinions wanted

Postby jmarsh » 2019-4-28 @ 07:26

Apparently OpenBSD has being using a crippled DOSBox for a while due to this issue:
https://www.reddit.com/r/openbsd_gaming ... ix_dosbox/
jmarsh
Member
 
Posts: 132
Joined: 2014-1-04 @ 09:17

Re: dynrec vs. secure platforms - opinions wanted

Postby jmarsh » 2019-5-17 @ 06:46

Guess there's not much interest in this so I'll just dump some patches in case they might be useful to someone in the future.
First patch: demonstrates approach 1 (lock/unlock) for windows using VirtualProtect(). Pretty basic stuff here.
Attachments
0001-implement-cache-lock-unlock-for-windows.patch
(2.13 KiB) Downloaded 6 times
jmarsh
Member
 
Posts: 132
Joined: 2014-1-04 @ 09:17

Re: dynrec vs. secure platforms - opinions wanted

Postby jmarsh » 2019-5-17 @ 11:15

Second patch: demonstrates approach 2 (multiple mappings of the same memory with different protection). This uses a specialized class for cache.pos with operator overloading to automatically return either the write pointer or execution pointer, depending on if the return value is unsigned/non-const (write pointer) or signed/const (exec pointer). I've tested this on linux x64 and am fairly sure it should also work with risc_armv8, but the other platforms might need some minor tweaking to ensure they cast to the correct type.
Attachments
mmap_prot.diff
(6.3 KiB) Downloaded 6 times
jmarsh
Member
 
Posts: 132
Joined: 2014-1-04 @ 09:17

Re: dynrec vs. secure platforms - opinions wanted

Postby Qbix » 2019-5-17 @ 16:02

Thanks!
I don't know at this point what is the best solution.. Maybe the authors of platform specific dynrec cores have an opinion on it.

Do you have any data on the performance ?
Water flows down the stream
How to ask questions the smart way!
User avatar
Qbix
DOSBox Author
 
Posts: 10825
Joined: 2002-11-27 @ 14:50
Location: Fryslan

Re: dynrec vs. secure platforms - opinions wanted

Postby jmarsh » 2019-5-17 @ 16:40

Qbix wrote:Do you have any data on the performance ?


The second patch (multiple mappings) seemed to give a slight performance boost over SVN but I can't really explain why. The mapped addresses were in similar ranges to what malloc returns so the same strategy would have been used for encoding 64-bit addresses (in gen_reg_memaddr and such).

The windows lock/unlock stuff I didn't compile in release mode so don't have accurate numbers.
jmarsh
Member
 
Posts: 132
Joined: 2014-1-04 @ 09:17


Return to DOSBox Development

Who is online

Users browsing this forum: Ringding and 0 guests