cros_sdk: use user rlimits inside the SDK
When cros_sdk switches to root via `sudo`, the rlimits are set to
root's policies. When we enter the chroot, we maintain those even
when we switch back to the user account. Since all code run via
cros_sdk is effectively "user code", restore the user rlimits when
we enter the chroot.
This doesn't guarantee the limits are high enough for whatever the
SDK tries to do, but there isn't much we can do about that. If the
system is configured with too low limits, the user and/or admin are
the ones who should resolve things.
BUG=b:234353695
TEST=`cros_sdk -- ulimit -a` reflects user settings instead of root
Change-Id: I0d08f725512e37e69a9d9257eb195073249b5681
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3721767
Reviewed-by: Ram Chandrasekar <rchandrasekar@google.com>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/cros_sdk.py b/scripts/cros_sdk.py
index ab50f20..7a4c9fa 100644
--- a/scripts/cros_sdk.py
+++ b/scripts/cros_sdk.py
@@ -12,6 +12,7 @@
"""
import argparse
+import ast
import glob
import logging
import os
@@ -193,6 +194,9 @@
cmd.append('--')
cmd.extend(additional_args)
+ if 'CHROMEOS_SUDO_RLIMITS' in os.environ:
+ _SetRlimits(os.environ.pop('CHROMEOS_SUDO_RLIMITS'))
+
# Some systems set the soft limit too low. Bump it up to the hard limit.
# We don't override the hard limit because it's something the admins put
# in place and we want to respect such configs. http://b/234353695
@@ -417,6 +421,42 @@
return snapshots
+# The rlimits we will lookup & pass down, in order.
+RLIMITS_TO_PASS = (
+ resource.RLIMIT_AS,
+ resource.RLIMIT_CORE,
+ resource.RLIMIT_CPU,
+ resource.RLIMIT_FSIZE,
+ resource.RLIMIT_MEMLOCK,
+ resource.RLIMIT_NICE,
+ resource.RLIMIT_NOFILE,
+ resource.RLIMIT_NPROC,
+ resource.RLIMIT_RSS,
+ resource.RLIMIT_STACK,
+)
+
+
+def _GetRlimits() -> str:
+ """Serialize current rlimits."""
+ return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
+
+
+def _SetRlimits(limits: str) -> None:
+ """Deserialize rlimits."""
+ for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
+ cur_limit = resource.getrlimit(rlim)
+ if cur_limit != limit:
+ # Turn the number into a symbolic name for logging.
+ name = 'RLIMIT_???'
+ for name, num in resource.__dict__.items():
+ if name.startswith('RLIMIT_') and num == rlim:
+ break
+ logging.debug('Restoring user rlimit %s from %r to %r',
+ name, cur_limit, limit)
+
+ resource.setrlimit(rlim, limit)
+
+
def _SudoCommand():
"""Get the 'sudo' command, along with all needed environment variables."""
@@ -434,6 +474,9 @@
# override the system's default PATH for root as that will hide /sbin.
cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
+ # Pass along current rlimit settings so we can restore them.
+ cmd += [f'CHROMEOS_SUDO_RLIMITS={_GetRlimits()}']
+
# Pass in the path to the depot_tools so that users can access them from
# within the chroot.
cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]