[RFC PATCH 02/20] execve: Add set_nnp_on_point_of_no_return

Justin Suess
2026-04-07T20:02:11Z

Allow LSM hooks to set a new bitfield in the binprm, ensuring
that the next execution will run with task_set_no_new_privs by executing
task_set_no_new_privs only past the point of no return.

This differs semantically from task_set_no_new_privs, which is not safe
to set from bprm_creds_for_exec/creds_from_file because a failed
execution will result in no_new_privs being set on the original task.
The setting of this flag from the LSM hook will not alter the current
task's no_new_privs field until after the point of no return, so if we
have a failed execution in execve there will be no side effect.

Setting this field will not result in any change to the escalation or
LSM checks for the current execution transition, only for subsequent
ones.

Signed-off-by: Justin Suess <utilityemal77@gmail.com>
---
fs/exec.c | 8 ++++++++
include/linux/binfmts.h | 7 ++++++-
2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/fs/exec.c b/fs/exec.c
index 9ea3a775d51e..6ab700af57d9 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1111,6 +1111,14 @@ int begin_new_exec(struct linux_binprm * bprm)
*/
bprm->point_of_no_return = true;

+ /*
+ * If requested that we set NO_NEW_PRIVS on the task, do so now that we're
+ * committed to exec. We set it here in case it wasn't safe to set it
+ * before the point of no return.
+ */
+ if (bprm->set_nnp_on_point_of_no_return)
+ task_set_no_new_privs(current);
+
/* Make this the only thread in the thread group */
retval = de_thread(me);
if (retval)
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 65abd5ab8836..9e420b055c4a 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -49,7 +49,12 @@ struct linux_binprm {
* Set by user space to check executability according to the
* caller's environment.
*/
- is_check:1;
+ is_check:1,
+ /*
+ * Set when a NNP should be applied to the new program's
+ * credentials during exec past the point of no return.
+ */
+ set_nnp_on_point_of_no_return:1;
struct file *executable; /* Executable to pass to the interpreter */
struct file *interpreter;
struct file *file;
--
2.53.0