===== RFC: Limiting Java exec process resources ===== The Java [[https://devdocs.io/openjdk~8/java/lang/processbuilder|ProcessBuilder]] class provides an interface for starting and communicating with a new process that runs outside the Java process that started it. Also see [[https://devdocs.io/openjdk~8/java/lang/runtime#exec-java.lang.String-|Runtime.exec]] method. > This new process is managed by the operating system, as part of process management. It has a self-contained execution environment, generally with a complete, private set of basic run-time resources: in particular, with its own memory space. ===== Why? ===== [[https://unix.stackexchange.com/questions/44985/limit-memory-usage-for-a-single-linux-process|This post]] describes a problem with ''pdftoppm'', one of the Poppler utilities also used by SavaPage: > "I'm running pdftoppm to convert a user-provided PDF into a 300DPI image. This works great, except if the user provides an PDF with a very large page size. pdftoppm will allocate enough memory to hold a 300DPI image of that size in memory, which for a 100 inch square page is 100*300 * 100*300 * 4 bytes per pixel = 3.5GB. A malicious user could just give me a silly-large PDF and cause all kinds of problems" Indeed, what will happen when an A0 sized PDF is offered for ''pdftoppm -png'' conversion, or perhaps an invalid PDF that will trigger a program bug that leads to excessive memory consumption? > We have to protect the SavaPage server process from excessive memory consumption of forked processes. ===== How? ===== ==== ulimit ==== With ''ulimit'' we can check and limit various process resources: # Check resources of user 'savapage' # -S (soft limits) of -a (all) resources sudo su - savapage -c "ulimit -Sa" > Soft limits are the current setting for a particular limit. They can be increased only to the current hard limit setting.\\ Display all current soft settings with: ''ulimit -Sa'' > Hard limits are the maximum limit that can be configured. Any changes to these require root access.\\ Display all current hard settings with: ''ulimit -Ha'' We focus on the ''virtual memory (kbytes, -v)'' resource, since the virtual address space is a good approximation for the memory used. Things get more complicated when a process spawns other processes: see [[http://coldattic.info/post/40/|Limiting time and memory consumption of a program in Linux]]. But we assume this is not the case for the simple programs used by SavaPage. Let's do some tests to get a feel of what ''ulimit'' does. Open a terminal session ... # Check ulimit -Sv # this will probably show "unlimited" # Set soft limit of virtual memory to 500MB # (the setting lives in this session only) ulimit -Sv 500000 # Check again ulimit -Sv > A simple tactic is to use ''ulimit'' at the invocation of the new child process, as shown below: # The 'sh' command simulates the Java exec. sh -c "ulimit -Sv 500000;pdftoppm -png -f 1 -l 1 test.pdf > test.png" # You can also throw cpu time (seconds, -t) in the mix sh -c "ulimit -Sv 500000;ulimit -St 2;pdftoppm -png -f 1 -l 1 test.pdf > test.png" ==== limits.conf ==== ''/etc/security/limits.conf'' holds the system wide limits for various users. The following lines could be added at the end of this file to set global virtual memory limits (the ''as'' keyword): savapage hard as 500000 savapage soft as 500000 ===== Possible solutions ===== ==== SavaPage Config ==== Configure ''ulimit'' properties for various exec commands in ''server.properties'' file. For example: # This property will resolve as: # ulimit -Sv 500000;ulimit -St 4;pdftoppm java.runtime.exec.ulimit.pdftoppm = -Sv 500000;-St 4 ==== System Config ==== Use ''/etc/security/limits.conf'' to limit virtual memory for user ''savapage''. === SysVinit === This will also limit the ''java'' process of the SavaPage server. Besides, ''java'' has its own command line options to allocate JVM memory. Therefore, set ''ulimit -Sv unlimited;'' when starting the ''java'' server process. === Systemd === systemd has its own way to limit memory to the ''java'' process: systemctl show savapage.service | grep MemoryLimit However, when java exec forks a child process, that process gets its ulimit values from ''/etc/security/limits.conf'' ===== Recommended solution ===== > Relying on one "System Config" ''limits.conf'' value for all types of forked child processes is too crude a mechanism. Therefore, //a more tailored approach with "SavaPage Config" is recommended//.