15

I have a pre-built userspace library that has an API along the lines of

void getBuffer (void **ppBuf, unsigned long *pSize);
void bufferFilled (void *pBuf, unsigned long size);

The idea being that my code requests a buffer from the lib, fills it with stuff, then hands it back to the lib.

I want another process to be able to fill this buffer. I can do this by creating some new shared buffer via shm*/shm_* APIs, have the other process fill that, then copy it to the lib's buffer in the lib's local process, but this has the overhead of an extra (potentially large) copy.

Is there a way to share memory that has ALREADY been mapped for a process? eg something like:

[local lib process]
getBuffer (&myLocalBuf, &mySize);
shmName = shareThisMemory (myLocalBuf, mySize);

[other process]
myLocalBuf = openTheSharedMemory (shmName);

That way the other process could write directly into the lib's buffer. (Synchronization between the processes is already taken care of so no problems there).

j0k
  • 22,600
  • 28
  • 79
  • 90
gimmeamilk
  • 2,016
  • 5
  • 24
  • 36

3 Answers3

11

There are good reasons for not allowing this functionality, particularly from the security side of things. A "share this mem" API would subvert the access permissions system.

Just assume an application holds some sort of critical/sensitive information in memory; the app links (via e.g. using a shared library, a preload, a modified linker/loader) to whatever component outside, and said component for the sheer fun of it decides to "share out the address space". It'd be a free-for-all, a method to bypass any sort of data access permission/restriction. You'd tunnel your way into the app.

Not good for your usecase, admitted, but rather justified from the system / application integrity point of view. Try searching the web for /proc/pid/mem mmap vulnerability for some explanation why this sort of access isn't wanted (in general).

If the library you use is designed to allow such shared access, it must itself provide the hooks to either allocate such a shared buffer, or use an elsewhere-preallocated (and possibly shared) buffer.

Edit: To make this clear, the process boundary is explicitly about not sharing the address space (amongst other things).
If you require a shared address space, either use threads (then the entire address space is shared and there's never any need to "export" anything), or explicitly set up a shared memory region in the same way as you'd set up a shared file.

Look at it from the latter point of view, two processes not opening it O_EXCL would share access to a file. But if one process already has it open O_EXCL, then the only way to "make it shared" (open-able to another process) is to close() it first then open() it again without O_EXCL. There's no other way to "remove" exclusive access from a file that you've opened as such other than to close it first.
Just as there is no way to remove exclusive access to a memory region mapped as such other than to unmap it first - and for a process' memory, MAP_PRIVATE is the default, for good reasons.

More: a process-shared memory buffer really isn't much different than a process shared file; using SysV-IPC style semantics, you have:

              | SysV IPC shared memory            Files
==============+===================================================================
creation      | id = shmget(key,..., IPC_CREAT);  fd = open("name",...,O_CREAT);
lookup        | id = shmget(key,...);             fd = open("name",...);
access        | addr = shmat(id,...);             addr = mmap(...,fd,...);
              |
global handle | IPC key                           filename
local handle  | SHM ID number                     filedescriptor number
mem location  | created by shmat()                created by mmap()

I.e. the key is the "handle" you're looking for, pass that the same way you would pass a filename, and both sides of the IPC connection can then use that key to check whether the shared resource exists, as well at access (attach to the handle) the contents though that.

FrankH.
  • 17,675
  • 3
  • 44
  • 63
  • Thanks for the reply. I'm working on an embedded system that has multiple processes communicating via IPC messaging. This works very well for most of the operations that require small messages to be passed back and forth, but processes also need to pass large buffers of data to each other. So for performance reasons I was hoping to have a special 'shortcut', whereby I could pass some shared memory handle in a message instead of having to copy the entire buffer (either via a pipe or another intermediate shared memory segment). – gimmeamilk Jun 23 '11 at 11:02
  • see edit; the handle to pass is the SHM IPC key. All users/sharers then use shmget/shmat to get the buffer address (within their address space). – FrankH. Jun 23 '11 at 13:14
  • Thanks FrankH. The problem unfortunately is that both those methods require me to be the creator of the memory, but I need to make the memory created by the opaque library shareable. – gimmeamilk Jun 23 '11 at 14:19
  • 1
    If you can control the kernel on your platform, then write a device driver that implements this via e.g. an `ioctl()` call, taking the PID and `[start, end]` range as arguments. You'd essentially be reimplementing `mmap()` on `/proc/PID/mem` - which got removed from the kernel almost ten years ago, as mentioned for security reasons; see http://www.securiteam.com/unixfocus/6V00F206AA.html for details. It's not impossible to get this, just not a good idea ... – FrankH. Jun 23 '11 at 17:12
  • @FrankH. The following link is down : www.securiteam.com/unixfocus/6V00F206AA.html – Semnodime Mar 15 '23 at 02:50
  • web.archive.org helps in such cases; the report I linked to is also still on https://seclists.org/vulnwatch/2002/q4/93 – FrankH. Mar 16 '23 at 07:01
5

A more modern way to share memory among processes is to use the POSIX shm_open() API.

Essentially, it's a portable way of putting files on a ramdisk (tmpfs). So one process uses shm_open plus ftruncate plus mmap. The other uses shm_open (with the same name) plus mmap plus shm_unlink. (With more than two processes, the last one to mmap it can unlink it.)

This way the shared memory will get reclaimed automatically when the last process exits; no need to explicitly remove the shared segment (as with SysV shared memory).

You still need to modify your application to allocate shared memory in this way, though.

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • The differences between POSIX `shm_*()` and old-style SysV-IPC-SHM are rather on the administrative side than the implementation / usage side (whether you use a sequence `shmctl(); shmget(); shmat(); shmdt()` or `shm_open(); ftruncate(); mmap(); shm_unlink()` to create/allocate/map/unmap seems down to personal preferences). Although the POSIX style is more in-line with the generic UNIX "make files if possible" approach. – FrankH. Jun 24 '11 at 08:06
  • @FrankH: Actually there is a huge difference... With SysV style shm, you have to manually delete (`IPC_RMID`) the segment _after you are finished with it_. With POSIX shm_*, you can `shm_unlink` the file _while you are still using the memory_. Just like ordinary files, the unlinked segment will continue to exist until the last process unmaps it, which will happen even if (say) the process crashes. With SysV SHM, crashes can leave shared memory "turds" behind. This never happens with POSIX shm_* if you simply unlink the file as early as possible. – Nemo Jun 24 '11 at 14:46
  • That's what I mean with "administrative side". Apart from that (the use of POSIX filesystem semantics for cleanup), there's very little difference. Whether you prefer to keep the segment around (because, say, for a 32GB database SGA it takes a while to recreate it, longer than to zero-fill it) or not is an _administrative_ choice. – FrankH. Jun 24 '11 at 17:13
  • @FrankH: Ah, got it. Fair enough. (Of course, POSIX shm_ gives you the choice of how to manage the shared memory; you can make it persistent simply by not unlinking the file.) Agree that the underlying implementation is essentially identical. – Nemo Jun 24 '11 at 18:31
-1

In theory at least, you can record the memory address of the buffer you got from your lib and have the other process mmap /proc/$PID_OF_FIRST_PROCCESS/mem file with the address as the offset.

I haven't tested it and I'm not sure /proc/PID/mem actually has an mmap file op implemented and there are a ton of security consideration but it might work. Best of luck :-)

gby
  • 14,900
  • 40
  • 57