Is it possible to develop a process without inheriting the virtual memory space of the parent process?

Since the parent process uses a huge array of memory, fork may terminate with errno ENOMEM with some configuration of the kernel overcommit policy. Despite the fact that a child process can only execute an exec program with low memory, for example ls.

To clarify the problem, if / proc / sys / vm / overcommit _memory is set to 2, (virtual) memory allocation is limited to SWAP + MEMORY * ration(default to 50%) . When the plug process, virtual memory is not copied thanks to COW. But the kernel still needs to allocate virtual memory space. As an analogue, fork is similar to malloc (virtual memory size), which will not allocate physical memory, and writing to shared memory will cause a copy of virtual memory and physical memory to be allocated. When overcommit_memory is set to 2, the fork may fail due to the allocation of virtual memory space.

Can a fork execute a process without inheriting the virtual memory space of the parent process under the following conditions?

  • if the child process calls exec after fork

  • if the child process does not call exec and will not use the global or static variable from the parent process. For example, a child process simply does some writing and then exits.

+8
source share
5 answers

No, It is Immpossible. You may be interested in vfork (2), which I do not recommend. Look also at mmap (2) and its flag MAP_NORESERVE . But the copy methods for recording are used by the kernel, so you practically do not double the memory consumption.

My suggestion is to have enough swap space so as not to be bothered by such a problem. Therefore, configure your computer to have more swap space available than the largest running process. You can always create some temporary paging file (for example, using dd if=/dev/zero of=/var/tmp/swapfile bs=1M count=32768 then mkswap/var/tmp/swapfile ) and then add it as swap time zone ( swapon/var/tmp/swapfile ) and delete it ( swapoff/var/tmp/swapfile and rm/var/tmp/swapfile ) when you no longer need it.

You probably do not want to swap the tmpfs file system, such as /tmp/ , since tmpfs file systems are backed up with swap space!.

I do not like excessive memory and I disable it (via proc (5) ). YMMV.

+7
source

I do not know how to do this (2), but for (1) you can try using vfork , which will wag a new process without copying the page tables of the parent process. But usually this is not recommended for a number of reasons, including because the parent element blocks until the child executes or completes.

+5
source

As Vasily Starynkevich answered , this is impossible.

However, a very simple and common solution is used for this, which does not depend on Linux behavior or control of excessive memory load: use an early forked subordinate process, do fork and exec.

Ask the large parent process to create a Unix domain socket and fork the slave process as early as possible by closing all other descriptors in the slave (reopening STDIN_FILENO , STDOUT_FILENO and STDERR_FILENO in /dev/null ). I prefer the datagram socket for its simplicity and guarantee, although the stream socket will also work.

In some rare cases, it is useful for the slave process to execute a separate, dedicated, small helper program. In most cases, this is not necessary and greatly simplifies security design. (On Linux, you can enable SCM_CREDENTIALS helper messages when transferring data through a Unix domain socket and use the process ID in it to verify the identity / executable file that uses the peer /proc/PID/exe pseudo- /proc/PID/exe /proc/PID/exe .)

In any case, the slave process will block reading from the socket. When the other end closes the socket, read / receive will return 0, and the slave process will end.

Each datagram that a subordinate process receives describes a command to execute. (Using a datagram allows you to use C strings separated by NUL characters without escaping, etc.; to use a Unix streaming socket, you usually need to delimit the β€œcommand” somehow, which in turn means escaping the delimiters in the strings of the command components.)

The slave process creates one or more channels and branches the child process. This child process closes the original Unix socket, replaces the standard threads with the corresponding ends of the channel (closing the other ends), and executes the required command. I personally prefer to use the optional close-on-exec socket on Linux to detect successful execution; in the event of an error, the errno code is written to the socket, so that the subordinate parent can reliably detect the failure and the exact cause. If successful, the subordinate parent closes the unnecessary ends of the channel, responds to the original process on successful completion, and the other end of the channel is SCM_RIGHTS auxiliary data. After sending a message, it closes the rest of the channel and waits for a new message.

On the side of the original process, the above process is sequential; only one thread can start an external process at a time. (You simply serialize access using the mutex.) Several may work simultaneously; this is just a request and response from a helper assistant that is serialized.

If this is a problem - it should not be in typical cases - you can, for example, multiplex connections by adding an ID prefix to each message (assigned by the parent process, monotonically increasing). In this case, you are likely to use a dedicated stream at the parent end to control communication with the slave, because you certainly cannot have multiple threads reading from the same socket at the same time and expect deterministic results.

Further circuit enhancements include things like using a dedicated process group for running processes, setting limits for them (by setting limits for a slave process), and executing commands as dedicated users and groups using a privileged slave.

In the case of a privileged slave, it is most useful for the parent node to perform a separate auxiliary process for it. On Linux, both parties can use SCM_CREDENTIALS helper messages via Unix domain sockets to verify the identity (PID and ID, executable file) of the peer, which makes it fairly simple to implement reliable security. (But note that /proc/PID/exe needs to be checked more than once to track attacks when a message is sent by a malicious program that quickly executes the corresponding program, but with command line arguments that lead to its quick completion, from time to time making it similar to the correct executable file that made the request, while a copy of the descriptor - and therefore the entire communication channel - was under the control of an unreliable user.)

Thus, the original problem can be solved, although the answer to the question is no. If executions are security sensitive, such as changing privileges (user accounts) or features (on Linux), then the project should be carefully thought out. considered, but in normal cases, the implementation is quite simple.

I would be happy to develop if necessary.

+4
source

This is possible on Linux. Use the clone system call without the CLONE_THREAD flag and with the CLONE_VM flag. Parent and child processes will use the same mappings as the thread; no COW or page table copying.

+1
source
 madvise(addr, size, MADV_DONTFORK) 

Alternatively, you can call munmap () after fork () to remove the virtual addresses inherited from the parent process.

0
source

All Articles