diff --git a/Dockerfile b/Dockerfile index 4de36a7bf..6ade466f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG BASE_IMAGE="${REGISTRY}/ubuntu:22.04" ARG VPN_VERSION="1.0.25" ARG BUSYBOX_VERSION="0.0.16" ARG LINUX_VERSION="3.5.33-beta" -ARG IGLOO_DRIVER_VERSION="0.0.77" +ARG IGLOO_DRIVER_VERSION="0.0.78" ARG LIBNVRAM_VERSION="0.0.26" ARG CONSOLE_VERSION="1.0.7" ARG GUESTHOPPER_VERSION="1.0.21" diff --git a/pyplugins/hyperfile/sysctl.py b/pyplugins/hyperfile/sysctl.py index 377f0504d..5055a39f4 100644 --- a/pyplugins/hyperfile/sysctl.py +++ b/pyplugins/hyperfile/sysctl.py @@ -62,6 +62,20 @@ def register_sysctl(self, sysctl_file: SysctlFile, path: Optional[str] = None): fname = sysctl_file.fs_relative_path + # Reject paths that cannot be safely registered as sysctls. These mostly + # come from auto-generated pseudofile models that scrape binary strings + # and emit bogus /proc/sys/* entries. Registering them is at best useless + # and at worst fatal: on older guest kernels (e.g. 4.10) asking the kernel + # to create a ctl_table under a filesystem-backed node such as + # /proc/sys/fs/binfmt_misc fails and then panics in the registration + # cleanup path. Skip them here rather than handing them to the guest; the + # igloo driver enforces the same invariant as a backstop. + reason = self._reject_reason(fname) + if reason is not None: + self.logger.warning( + f"Skipping sysctl registration for '{fname}': {reason}") + return + # ENFORCE SINGLE REGISTRATION if fname in self._sysctls: raise ValueError( @@ -74,6 +88,31 @@ def register_sysctl(self, sysctl_file: SysctlFile, path: Optional[str] = None): self._pending_sysctls.append((fname, sysctl_file)) self._sysctls[fname] = sysctl_file + # Subtrees of /proc/sys that are not sysctls at all but separate + # filesystems mounted there (binfmt_misc is the canonical example). The + # guest kernel will not let us register a ctl_table inside them, and on old + # kernels the failed attempt panics. Modeling them as sysctls is always wrong. + _NON_SYSCTL_SUBTREES = ("fs/binfmt_misc",) + + def _reject_reason(self, fname: str) -> Optional[str]: + """ + Return a human-readable reason if ``fname`` (a sysctl-root-relative path + such as ``net/ipv4/ip_forward``) must not be registered as a sysctl, or + None if it is acceptable. + """ + rel = fname.strip("/") + if not rel: + return "empty sysctl path" + components = rel.split("/") + if any(c == "" for c in components): + # e.g. an embedded "//" produces an empty path component, which the + # kernel cannot turn into a ctl_dir. + return "path contains an empty component" + for subtree in self._NON_SYSCTL_SUBTREES: + if rel == subtree or rel.startswith(subtree + "/"): + return f"/proc/sys/{subtree} is a mounted filesystem, not a sysctl" + return None + def _split_sysctl_path(self, path: str): """ Split a sysctl path into (dir_path, entry_name).