From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f175.google.com (mail-pg1-f175.google.com [209.85.215.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FE2541A931 for ; Fri, 24 Jul 2026 22:02:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.175 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784930554; cv=none; b=o+Yov7vFRkZ/l4UCXrd9LzmRoKv2O36ld1Uue7Skzox5tZi+XBoYWkLHVMQhYNHVSXpmVK5i7+UaFlnBIjolj1958cw8qHN/59orG4aJZ1u1clFy9pB/hj8L6ABGsU1fkvcBipR3XYsyFf+s+Oue0Yc8IX6JukMgbTs5lTcXHEs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784930554; c=relaxed/simple; bh=5HCIi5ySWkKQMRa9Wr9spZFtfcF1MXpOzBTg6XYfGww=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=INyNckSahPGWT9ul3dn/L7fAjA52OeqIBNoEJiMfTJP8Cj1E8x1rfCaUHnkM7JglvoRhi4j/oUw5TCis+dBpKw4vTdtUVzH1XxfhWJV6whRjJi9rOiJu+EElsPkSbCDTV05mx83B31r/uISUyLMcoBHdPmJmkoxZisMebrrUTaQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=rPMGYTcA; arc=none smtp.client-ip=209.85.215.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="rPMGYTcA" Received: by mail-pg1-f175.google.com with SMTP id 41be03b00d2f7-c9d1fff21edso590602a12.1 for ; Fri, 24 Jul 2026 15:02:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784930552; x=1785535352; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=e1GNNEUTrXfcasiBfiMqOJUDgRwUpOC+gV6LgyALCys=; b=rPMGYTcAVhKyfnbu0fBbhxfdTbEu5GGQ6TsLw6S2glwO3Ta2GxwnyE/cCUa51sd2Nd i9rZsTc41Ycz/3fRnUG8G+ZZoBtfFFw2SKMLG+INSaThLgXx/trQnDVjsT9+kwvYZSU+ GrxuxVMf0iVNmLUN204CX6DQF1ZVThS97XbMVSl8DGANzA6s1TdGsRYHTlPeHEshzPmr rL21szI+JHZkgRwXhQFVxMhbK8qmFxAWg5uHYO0gfGudRaXs8JD5N1QQKYqrOgpvmUu5 4nVC81bzWQLVrcSE/7kCYOyORrhl6Ca2hNt7JSyg9KiYdyerR8Ozi+WvV5kATV+9lnX4 m9pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784930552; x=1785535352; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=e1GNNEUTrXfcasiBfiMqOJUDgRwUpOC+gV6LgyALCys=; b=jMVeOKT38H4CYAQS/MbXlocdPVgtMku2pchFAh18huiP2oUPyiOqWC7e/YfH8M2xuU FBRE9S1rgMGQzBK5YFq9etwfdnQAMgGfRmBchELXXmu3+iQ0zo1ZJK8qQkcs9CSIQb+b 6tGtneUjZk3MSHrnzRDOmsbSWI1moCHcgHXLUw7u+/z+ePMhVVFxENNpTYlaTcAaZ+/3 4JNdGodhWR3SkFPzMRjP1DG2cRL8u8S4uueRZFHmEp0GfoHPOxTP6rAy1bLKIrPgfI64 DjcUMXyzcBV0EvzrqJ7q4Df5gUGy4DWIhiiRoM//jm2tpiWB38mWWT8kngTgnMwmldLj sH9A== X-Forwarded-Encrypted: i=1; AHgh+RqW7FoZX81SmLgX8LAIJcQIk3r0rDetKag0PnvUHYVWNN0MswBqrMKk5YMRoo7JMVI+trVE7O+7pnXnWg8=@vger.kernel.org X-Gm-Message-State: AOJu0YzUbJw8alD8/h8uafRrbKz6up+ISu+mqWEY3HPYBuZr2vl450Qw XlnQz4u1/b7p1ruM4SnXb8HoUSjIBX32FRak8+oMELuulyBPt1O5KPjr X-Gm-Gg: AR+sD10fmtTSYyW23C0QlTap3Iy0ygh4+tNYKKgG6TxgyN0ua7+Kq6kTYpuGUBTWTOX jlaCUbwAUsjogJVGhJWHqDheF9DVbpncuI863cgXqOAkiRDXlaLQxhJdnJ4O/Lj+P7EeQ9RH+gl M4IZe3pgYediHT83fp1k22MNoq4+VgYuiI7y5GhXXwA2AE6f2wJX9r6k2nEt/ouiJVwyQOUjWNo aCj7aDv5S+v7cvsh6DX0CGmK9Qe0VRMrxViW4wxWU7dkR/iwtn+3Ig7RayNQdHG/Iyyu7ty8HOl Vc30a/BfxiyJKtBRelOyzq9Pwxjl0VCxywTFaxITD4nnBTgxEFzKVQDt1SxHB67NcIC9/7qIExM 5bYjJH6SFeWYGxOO1SvhFwE3tdWzDr/p1nFyqlxIklbsebJ4CYUUm3NiBrKczX8lQm6Q0G/ozfy OI2X4jHexOAG8kHIHuxhB4AMBndnwGIOQ93HDbX5o2un/6P2KCbPqCTGg6EzwA X-Received: by 2002:a05:6a21:6e96:b0:3c0:9c1a:8941 with SMTP id adf61e73a8af0-3c67e17d26dmr101841637.73.1784930551744; Fri, 24 Jul 2026 15:02:31 -0700 (PDT) Received: from pop-os.tail4adac7.ts.net ([2601:647:6802:dbc0:546a:e1b0:9574:3a29]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-314bc5a67f3sm2962631eec.29.2026.07.24.15.02.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2026 15:02:30 -0700 (PDT) From: Cong Wang To: Andy Lutomirski Cc: Kees Cook , linux-kernel@vger.kernel.org, Will Drewry , Christian Brauner , Andrew Morton , linux-mm@kvack.org, Cong Wang Subject: [PATCH v7 7/8] docs/seccomp: document pinned-memfd redirect ioctls Date: Fri, 24 Jul 2026 15:01:46 -0700 Message-ID: <20260724220147.214396-8-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260724220147.214396-1-xiyou.wangcong@gmail.com> References: <20260724220147.214396-1-xiyou.wangcong@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Cong Wang Document SECCOMP_IOCTL_NOTIF_PIN_INSTALL and SECCOMP_IOCTL_NOTIF_SEND_REDIRECT in the userspace API guide: the SECCOMP_FILTER_FLAG_REDIRECT opt-in and the single-redirector restriction, the two response structures, and how the pair closes the user-notification TOCTOU for non-cooperative fork+execve sandboxes. Also spell out the scope the implementation deliberately enforces or relies on: read-only input pointers only, same-syscall-number only (rt_sigreturn and the clone/fork family are refused), the per-interruption re-notification of restartable syscalls and the restart-block behaviour, and the ptrace syscall-stop semantics. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- .../userspace-api/seccomp_filter.rst | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/Documentation/userspace-api/seccomp_filter.rst b/Documentation/userspace-api/seccomp_filter.rst index cff0fa7f3175..be2c338224ac 100644 --- a/Documentation/userspace-api/seccomp_filter.rst +++ b/Documentation/userspace-api/seccomp_filter.rst @@ -289,6 +289,115 @@ above in this document: all arguments being read from the tracee's memory should be read into the tracer's memory before any policy decisions are made. This allows for an atomic decision on syscall arguments. +Non-cooperative pinned-memfd redirect +===================================== + +The TOCTOU described above means ``SECCOMP_USER_NOTIF_FLAG_CONTINUE`` cannot +enforce a policy on pointer arguments: after the supervisor inspects the +target's memory and lets the syscall continue, the target (or a thread sharing +its address space) can rewrite that memory before the kernel reads it. The +cooperative workaround, the target ``mmap()`` + ``mseal()``-ing a shared +buffer, is unavailable in the fork+execve sandbox model, where the supervisor +confines a binary it did not write. + +Two ioctls let the supervisor close this race without target cooperation. The +redirect step (below) requires a listener created with +``SECCOMP_FILTER_FLAG_REDIRECT`` (in addition to +``SECCOMP_FILTER_FLAG_NEW_LISTENER``). Because it rewrites another task's +registers, at most one such listener may exist in a task's filter chain; a +second fails with ``-EBUSY``: + +.. code-block:: c + + fd = seccomp(SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_NEW_LISTENER | SECCOMP_FILTER_FLAG_REDIRECT, + &prog); + +``ioctl(SECCOMP_IOCTL_NOTIF_PIN_INSTALL)`` installs a sealed mapping of a +supervisor-owned ``memfd`` directly into the trapped task's address space: + +.. code-block:: c + + struct seccomp_notif_pin_install { + __u64 id; + __u32 flags; /* reserved, must be 0 */ + __u32 memfd; + __u64 target_addr; + __u64 size; + __u64 offset; /* page-aligned offset into memfd */ + }; + +``id`` names an active notification (the trapped task to install into). +``target_addr``, ``size`` and ``offset`` are page-aligned; ``offset`` selects +where in ``memfd`` the mapping starts, so one memfd can back several pins. If +``target_addr`` is ``0`` the kernel picks a free address and writes it back; +otherwise an existing mapping there yields ``-EEXIST``. The pin is read-only +and sealed, the target and its threads cannot unmap, move, reprotect or +overwrite it, and lasts until the target calls ``execve()`` or exits. + +``memfd`` must be write-sealed (``F_SEAL_WRITE`` or ``F_SEAL_FUTURE_WRITE``) +or the ioctl returns ``-EINVAL``; otherwise the target could rewrite the pin's +bytes through a separate writable handle to the same memfd. +``F_SEAL_FUTURE_WRITE`` still lets the supervisor update the contents through +its own mapping made before the seal. + +``ioctl(SECCOMP_IOCTL_NOTIF_SEND_REDIRECT)`` then resumes the trapped syscall +like ``SECCOMP_USER_NOTIF_FLAG_CONTINUE``, but with selected argument +registers replaced: + +.. code-block:: c + + struct seccomp_notif_resp_redirect { + __u64 id; + __u32 flags; /* SECCOMP_REDIRECT_FLAG_CONTINUE must be set */ + __u32 args_mask; /* which arg registers to replace */ + __u32 ptr_mask; /* which of those are pointers into a pin */ + __u32 memfd; /* the pin's backing memfd */ + __u64 args[6]; /* replacement values */ + __u64 ptr_len[6]; /* validated access length for each pointer arg */ + }; + +Each bit in ``ptr_mask`` (a subset of ``args_mask``) marks ``args[i]`` as a +pointer; the access ``[args[i], args[i] + ptr_len[i])`` must lie within a +single read-only pin of ``memfd`` in the target, or the ioctl returns +``-EFAULT``. ``ptr_len[i]`` must be non-zero for those bits and ``0`` +otherwise. Bits in ``args_mask`` but not ``ptr_mask`` are scalar replacements +written verbatim, e.g. to set the length register that goes with a redirected +pointer. The original registers are restored at syscall exit, so the +substitution is invisible to the target and the TOCTOU is closed. + +Scope and limitations +--------------------- + +The redirect mechanism is deliberately narrow and is *not* a general syscall +rewriting facility: + +- **Read-only input pointers only.** A pin is read-only, so only an argument + the syscall *reads* (a pathname, a ``sockaddr``) may be redirected into it. + Aiming an output or in/out argument at a pin makes the syscall fail with + ``-EFAULT`` when it writes back. + +- **Same syscall only.** A redirect replaces arguments, never the syscall + number. ``rt_sigreturn()`` (and its compat variant) cannot be redirected and + return ``-EOPNOTSUPP``. + +- **Signals and restarts.** The redirected syscall really runs, so it can be + interrupted and restarted. On a restart the original arguments are restored + and the syscall re-traps, so the supervisor is notified again and must answer + consistently. Syscalls the kernel restarts without re-trapping (e.g. + ``nanosleep()``, ``futex(FUTEX_WAIT)``) keep the substituted arguments -- + safe for read-only inputs, but a reason not to redirect arguments of syscalls + that block or wait. + +- **clone()/fork().** The task-creation family (``clone``, ``clone3``, + ``fork``, ``vfork``) cannot be redirected and returns ``-EOPNOTSUPP``. A new + child inherits the substituted argument registers but not the restore, so it + would return to user space with the pinned values still in its registers. + +- **ptrace.** A tracer sees the substituted arguments at the syscall-exit stop; + they are restored before the task resumes, so a ``PTRACE_SETREGS`` of a + substituted register at that stop is overwritten. + Sysctls ======= -- 2.43.0