Suppose my non-root 32-bit app runs on a 64-bit system, all filesystems of which are mounted as read-only. The app creates an image of a 64-bit ELF in memory. But due to read-only filesystems it can't dump this image to a file to do an execve on. Is there still a supported way to launch a process from this image?

Note: the main problem here is to switch from 32-bit mode to 64-bit, not doing any potentially unreliable hacks. If this is solved, then the whole issue becomes trivial — just make a custom loader.

  • If you have a tmpfs, you could write the image there and execute it. tmpfs is backed entirely by memory. Not sure this fits your requirements, though.
    – alienth
    Sep 18 '15 at 6:40
  • @alienth to mount a tmpfs I'd need root privileges.
    – Ruslan
    Sep 18 '15 at 10:21
  • Ruslan: @alienth's suggestion is to keep a tmpfs filesystem permanently mounted. This doesn't let any of the read-only memory pages in your 64bit process be backed by the contents of the disk file holding the 32bit executable, though. Instead, everything it needs has to be copied. (Unless you have the 64bit program open and mmap after it starts.) Also, you'd need to make sure you clean up the tmpfs, to avoid upward-creeping memory usage. Sep 18 '15 at 17:39

Yes, via memfd_create and fexecve:

int fd = memfd_create("foo", MFD_CLOEXEC);
// write your image to fd however you want
fexecve(fd, argv, envp);
  • 1
    Great. I've checked, this indeed works (see this example code (the child simply spins increasing rax)). But a couple of comments: memfd_create first appears in Linux 3.17. Include <sys/mman.h>, having defined _GNU_SOURCE (man page appears wrong about the include and doesn't mention the need for the define).
    – Ruslan
    Sep 3 '18 at 21:01
  • @Ruslan Good find. I just sent in a patch to add the _GNU_SOURCE requirement to the man page. (The header name is already fixed in git; the website just hasn't updated yet.) Sep 3 '18 at 22:38

You're looking for something like "userland exec". Implementation here. Basically, this involves loading some position-independent code that has no external references into memory, and marking it executable. This position independent code removes the previously-running executable, and reloads. Sounds like you might have to modify the userland exec I wrote at least a little.

  • 1
    IDK if you can change from compat (32bit) mode to long (64bit) mode other than with execve. It's going to require a kernel call of some sort. personality(2) sounds like it might be the right thing, but it's for stuff like changing signal numbers to emulate a BSD or other non-Linux system call ABI, I think. man7.org/linux/man-pages/man2/personality.2.html Sep 18 '15 at 17:44
  • 1
    The only way to switch from 32bit to 64bit mode I've come up with is quite hacky. One can't even guarantee that it won't break in a newer kernel. Thus the implementation you point to doesn't seem workable in my use case. And, even if I do use the above mentioned hack, the process still looks like 32 bit to the kernel: e.g. ptrace'ing it with GETREGSET with NT_PRSTATUS gives 32-bit regset.
    – Ruslan
    Sep 18 '15 at 18:42
  • As far as I can tell this doesn't close CLOEXEC fds Feb 21 '19 at 20:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.