* [PATCH v3 2/7] treewide: Get rid of get_task_comm()
From: André Almeida @ 2026-06-12 16:20 UTC (permalink / raw)
To: Peter Zijlstra, Juri Lelli, Vincent Guittot, Steven Rostedt,
Christian Brauner, Kees Cook, Shuah Khan, willy,
mathieu.desnoyers, David Laight, Linus Torvalds, akpm,
Yafang Shao, andrii.nakryiko, arnaldo.melo, Petr Mladek
Cc: linux-kernel, kernel-dev, linux-mm, linux-api, André Almeida
In-Reply-To: <20260612-tonyk-long_name-v3-0-7989b66e8a99@igalia.com>
Since commit 4cc0473d7754 ("get rid of __get_task_comm()"),
get_task_comm() does just a redundant check for the buffer size and call
strscpy_pad(). Replace get_task_comm() calls with strscpy_pad(), that will
do the right thing if the buffers sizes doesn't match: zero-pad if it's
bigger, and truncate if it's smaller.
Link: https://lore.kernel.org/lkml/CAHk-=wi5c=_-FBGo_88CowJd_F-Gi6Ud9d=TALm65ReN7YjrMw@mail.gmail.com/
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
Changes from v1:
- Fix for security/ipe/audit.c and net/netfilter/nf_tables_api.c
---
drivers/connector/cn_proc.c | 2 +-
drivers/dma-buf/sw_sync.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 4 ++--
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 2 +-
drivers/gpu/drm/lima/lima_ctx.c | 2 +-
drivers/gpu/drm/panfrost/panfrost_gem.c | 2 +-
drivers/gpu/drm/panthor/panthor_gem.c | 2 +-
drivers/gpu/drm/panthor/panthor_sched.c | 2 +-
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 2 +-
drivers/hwtracing/stm/core.c | 2 +-
drivers/tty/tty_audit.c | 2 +-
fs/binfmt_elf.c | 2 +-
fs/binfmt_elf_fdpic.c | 2 +-
fs/proc/array.c | 2 +-
include/linux/sched.h | 19 -------------------
kernel/audit.c | 6 ++++--
kernel/auditsc.c | 6 ++++--
kernel/printk/printk.c | 2 +-
kernel/sys.c | 2 +-
net/bluetooth/hci_sock.c | 2 +-
net/netfilter/nf_tables_api.c | 4 +++-
security/integrity/integrity_audit.c | 3 ++-
security/ipe/audit.c | 3 ++-
security/landlock/domain.c | 2 +-
security/lsm_audit.c | 7 ++++---
29 files changed, 42 insertions(+), 52 deletions(-)
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 0056ab81fbc3..c78243ed3c2a 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -278,7 +278,7 @@ void proc_comm_connector(struct task_struct *task)
ev->what = PROC_EVENT_COMM;
ev->event_data.comm.process_pid = task->pid;
ev->event_data.comm.process_tgid = task->tgid;
- get_task_comm(ev->event_data.comm.comm, task);
+ strscpy_pad(ev->event_data.comm.comm, task->comm);
memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
msg->ack = 0; /* not used */
diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c
index 8df20b0218a9..d501657ad801 100644
--- a/drivers/dma-buf/sw_sync.c
+++ b/drivers/dma-buf/sw_sync.c
@@ -312,7 +312,7 @@ static int sw_sync_debugfs_open(struct inode *inode, struct file *file)
struct sync_timeline *obj;
char task_comm[TASK_COMM_LEN];
- get_task_comm(task_comm, current);
+ strscpy_pad(task_comm, current->comm);
obj = sync_timeline_create(task_comm);
if (!obj)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
index 6a364357522b..13c8857e4ffb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
@@ -74,7 +74,7 @@ struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
/* This reference gets released in amdkfd_fence_release */
mmgrab(mm);
fence->mm = mm;
- get_task_comm(fence->timeline_name, current);
+ strscpy_pad(fence->timeline_name, current->comm);
spin_lock_init(&fence->lock);
fence->svm_bo = svm_bo;
fence->context_id = context_id;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
index 4c5e38dea4c2..faf0f36d8328 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
@@ -129,7 +129,7 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr *evf_mgr,
return -ENOMEM;
ev_fence->evf_mgr = evf_mgr;
- get_task_comm(ev_fence->timeline_name, current);
+ strscpy_pad(ev_fence->timeline_name, current->comm);
spin_lock_init(&ev_fence->lock);
dma_fence_init64(&ev_fence->base, &amdgpu_eviction_fence_ops,
&ev_fence->lock, evf_mgr->ev_fence_ctx,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
index 6c644cfe6695..c45630457155 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
@@ -4419,7 +4419,7 @@ int amdgpu_ras_init(struct amdgpu_device *adev)
}
con->init_task_pid = task_pid_nr(current);
- get_task_comm(con->init_task_comm, current);
+ strscpy_pad(con->init_task_comm, current->comm);
mutex_init(&con->critical_region_lock);
INIT_LIST_HEAD(&con->critical_region_head);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index e2d5f04296e1..8fdc38d8d64d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -85,7 +85,7 @@ int amdgpu_userq_fence_driver_alloc(struct amdgpu_device *adev,
fence_drv->adev = adev;
fence_drv->context = dma_fence_context_alloc(1);
- get_task_comm(fence_drv->timeline_name, current);
+ strscpy_pad(fence_drv->timeline_name, current->comm);
*fence_drv_req = fence_drv;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 9ba9de16a27a..de80d0ace905 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -2571,10 +2571,10 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm)
return;
vm->task_info->task.pid = current->pid;
- get_task_comm(vm->task_info->task.comm, current);
+ strscpy_pad(vm->task_info->task.comm, current->comm);
vm->task_info->tgid = current->tgid;
- get_task_comm(vm->task_info->process_name, current->group_leader);
+ strscpy_pad(vm->task_info->process_name, current->group_leader->comm);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index 2a241a5b12c4..f8ce59d8587a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -563,7 +563,7 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
}
vres->task.pid = task_pid_nr(current);
- get_task_comm(vres->task.comm, current);
+ strscpy_pad(vres->task.comm, current->comm);
list_add_tail(&vres->vres_node, &mgr->allocated_vres_list);
if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS && adjust_dcc_size) {
diff --git a/drivers/gpu/drm/lima/lima_ctx.c b/drivers/gpu/drm/lima/lima_ctx.c
index 68ede7a725e2..e8c5c3601bf1 100644
--- a/drivers/gpu/drm/lima/lima_ctx.c
+++ b/drivers/gpu/drm/lima/lima_ctx.c
@@ -29,7 +29,7 @@ int lima_ctx_create(struct lima_device *dev, struct lima_ctx_mgr *mgr, u32 *id)
goto err_out0;
ctx->pid = task_pid_nr(current);
- get_task_comm(ctx->pname, current);
+ strscpy_pad(ctx->pname, current->comm);
return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 3a7fce428898..11936c4d3573 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -36,7 +36,7 @@ static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev,
struct panfrost_gem_object *bo)
{
bo->debugfs.creator.tgid = current->tgid;
- get_task_comm(bo->debugfs.creator.process_name, current->group_leader);
+ strscpy_pad(bo->debugfs.creator.process_name, current->group_leader->comm);
mutex_lock(&pfdev->debugfs.gems_lock);
list_add_tail(&bo->debugfs.node, &pfdev->debugfs.gems_list);
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index cd49859da89b..b44fd715c17e 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -46,7 +46,7 @@ static void panthor_gem_debugfs_bo_add(struct panthor_gem_object *bo)
struct panthor_device, base);
bo->debugfs.creator.tgid = current->tgid;
- get_task_comm(bo->debugfs.creator.process_name, current->group_leader);
+ strscpy_pad(bo->debugfs.creator.process_name, current->group_leader->comm);
mutex_lock(&ptdev->gems.lock);
list_add_tail(&bo->debugfs.node, &ptdev->gems.node);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 2fe04d0f0e3a..8ee9de96acf6 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3603,7 +3603,7 @@ static void group_init_task_info(struct panthor_group *group)
struct task_struct *task = current->group_leader;
group->task_info.pid = task->pid;
- get_task_comm(group->task_info.comm, task);
+ strscpy_pad(group->task_info.comm, task->comm);
}
static void add_group_kbo_sizes(struct panthor_device *ptdev,
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index c33c057365f8..d2bf221e8f01 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -50,7 +50,7 @@ static void virtio_gpu_create_context_locked(struct virtio_gpu_device *vgdev,
} else {
char dbgname[TASK_COMM_LEN];
- get_task_comm(dbgname, current);
+ strscpy_pad(dbgname, current->comm);
virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id,
vfpriv->context_init, strlen(dbgname),
dbgname);
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index f48c6a8a0654..c7715439964e 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -634,7 +634,7 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
char comm[sizeof(current->comm)];
char *ids[] = { comm, "default", NULL };
- get_task_comm(comm, current);
+ strscpy_pad(comm, current->comm);
err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1);
/*
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index d014af6ab060..d514a81d0a5c 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -77,7 +77,7 @@ static void tty_audit_log(const char *description, dev_t dev,
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d minor=%d comm=",
description, pid, uid, loginuid, sessionid,
MAJOR(dev), MINOR(dev));
- get_task_comm(name, current);
+ strscpy_pad(name, current->comm);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
audit_log_n_hex(ab, data, size);
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 16a56b6b3f6c..d25922460b63 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1557,7 +1557,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
rcu_read_unlock();
- get_task_comm(psinfo->pr_fname, p);
+ strscpy_pad(psinfo->pr_fname, p->comm);
return 0;
}
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 7e3108489c83..c4d4e59ff34d 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1371,7 +1371,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
SET_UID(psinfo->pr_uid, from_kuid_munged(cred->user_ns, cred->uid));
SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
rcu_read_unlock();
- get_task_comm(psinfo->pr_fname, p);
+ strscpy_pad(psinfo->pr_fname, p->comm);
return 0;
}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 90fb0c6b5f99..c8c3fbd9bfa9 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -110,7 +110,7 @@ void proc_task_name(struct seq_file *m, struct task_struct *p, bool escape)
else if (p->flags & PF_KTHREAD)
get_kthread_comm(tcomm, sizeof(tcomm), p);
else
- get_task_comm(tcomm, p);
+ strscpy_pad(tcomm, p->comm);
if (escape)
seq_escape_str(m, tcomm, ESCAPE_SPACE | ESCAPE_SPECIAL, "\n\\");
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 60d004a49a27..b6de742b1155 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2000,25 +2000,6 @@ extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec
__set_task_comm(tsk, from, false); \
})
-/*
- * - Why not use task_lock()?
- * User space can randomly change their names anyway, so locking for readers
- * doesn't make sense. For writers, locking is probably necessary, as a race
- * condition could lead to long-term mixed results.
- * The logic inside __set_task_comm() ensures that the task comm is
- * always NUL-terminated and zero-padded. Therefore the race condition between
- * reader and writer is not an issue.
- *
- * - BUILD_BUG_ON() can help prevent the buf from being truncated.
- * Since the callers don't perform any return value checks, this safeguard is
- * necessary.
- */
-#define get_task_comm(buf, tsk) ({ \
- BUILD_BUG_ON(sizeof(buf) < TASK_COMM_LEN); \
- strscpy_pad(buf, (tsk)->comm); \
- buf; \
-})
-
static __always_inline void scheduler_ipi(void)
{
/*
diff --git a/kernel/audit.c b/kernel/audit.c
index e1d489bc2dff..6fc867adbf3d 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1662,7 +1662,8 @@ static void audit_log_multicast(int group, const char *op, int err)
audit_put_tty(tty);
audit_log_task_context(ab); /* subj= */
audit_log_format(ab, " comm=");
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
audit_log_d_path_exe(ab, current->mm); /* exe= */
audit_log_format(ab, " nl-mcgrp=%d op=%s res=%d", group, op, !err);
audit_log_end(ab);
@@ -2465,7 +2466,8 @@ void audit_log_task_info(struct audit_buffer *ab)
audit_get_sessionid(current));
audit_put_tty(tty);
audit_log_format(ab, " comm=");
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
audit_log_d_path_exe(ab, current->mm);
audit_log_task_context(ab);
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index ab54fccba215..8e4f70105a13 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2877,7 +2877,8 @@ void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
audit_log_format(ab, " pid=%u", task_tgid_nr(current));
audit_log_task_context(ab); /* subj= */
audit_log_format(ab, " comm=");
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
audit_log_end(ab);
}
EXPORT_SYMBOL_GPL(__audit_log_nfcfg);
@@ -2900,7 +2901,8 @@ static void audit_log_task(struct audit_buffer *ab)
sessionid);
audit_log_task_context(ab);
audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
audit_log_d_path_exe(ab, current->mm);
}
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 0323149548f6..1f04e753ca02 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2247,7 +2247,7 @@ static u16 printk_sprint(char *text, u16 size, int facility,
static void printk_store_execution_ctx(struct printk_info *info)
{
info->caller_id2 = printk_caller_id2();
- get_task_comm(info->comm, current);
+ strscpy_pad(info->comm, current->comm);
}
static void pmsg_load_execution_ctx(struct printk_message *pmsg,
diff --git a/kernel/sys.c b/kernel/sys.c
index 62e842055cc9..1d5152d2395e 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2609,7 +2609,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
proc_comm_connector(me);
break;
case PR_GET_NAME:
- get_task_comm(comm, me);
+ strscpy_pad(comm, me->comm);
if (copy_to_user((char __user *)arg2, comm, sizeof(comm)))
return -EFAULT;
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 0290dea081f6..38e16ba2de38 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -106,7 +106,7 @@ static bool hci_sock_gen_cookie(struct sock *sk)
id = 0xffffffff;
hci_pi(sk)->cookie = id;
- get_task_comm(hci_pi(sk)->comm, current);
+ strscpy_pad(hci_pi(sk)->comm, current->comm);
return true;
}
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 87387adbca65..cd00d4da1316 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -9709,9 +9709,11 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
if (!nlh)
goto nla_put_failure;
+ strscpy_pad(buf, current->comm);
+
if (nla_put_be32(skb, NFTA_GEN_ID, htonl(nft_base_seq(net))) ||
nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) ||
- nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current)))
+ nla_put_string(skb, NFTA_GEN_PROC_NAME, buf))
goto nla_put_failure;
nlmsg_end(skb, nlh);
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index d8d9e5ff1cd2..98060060929d 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -54,7 +54,8 @@ void integrity_audit_message(int audit_msgno, struct inode *inode,
audit_get_sessionid(current));
audit_log_task_context(ab);
audit_log_format(ab, " op=%s cause=%s comm=", op, cause);
- audit_log_untrustedstring(ab, get_task_comm(name, current));
+ strscpy_pad(name, current->comm);
+ audit_log_untrustedstring(ab, name);
if (fname) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index 93fb59fbddd6..90a6acfb7cdf 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -145,7 +145,8 @@ void ipe_audit_match(const struct ipe_eval_ctx *const ctx,
audit_log_format(ab, "ipe_op=%s ipe_hook=%s enforcing=%d pid=%d comm=",
op, audit_hook_names[ctx->hook], READ_ONCE(enforce),
task_tgid_nr(current));
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
if (ctx->file) {
audit_log_d_path(ab, " path=", &ctx->file->f_path);
diff --git a/security/landlock/domain.c b/security/landlock/domain.c
index 06b6bd845060..a35a27f523e6 100644
--- a/security/landlock/domain.c
+++ b/security/landlock/domain.c
@@ -101,7 +101,7 @@ static struct landlock_details *get_current_details(void)
memcpy(details->exe_path, path_str, path_size);
details->pid = get_pid(task_tgid(current));
details->uid = from_kuid(&init_user_ns, current_uid());
- get_task_comm(details->comm, current);
+ strscpy_pad(details->comm, current->comm);
return details;
}
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 737f5a263a8f..a587ffecd985 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -276,8 +276,8 @@ void audit_log_lsm_data(struct audit_buffer *ab,
if (pid) {
char tskcomm[sizeof(tsk->comm)];
audit_log_format(ab, " opid=%d ocomm=", pid);
- audit_log_untrustedstring(ab,
- get_task_comm(tskcomm, tsk));
+ strscpy_pad(tskcomm, tsk->comm);
+ audit_log_untrustedstring(ab, tskcomm);
}
}
break;
@@ -417,7 +417,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
char comm[sizeof(current->comm)];
audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
- audit_log_untrustedstring(ab, get_task_comm(comm, current));
+ strscpy_pad(comm, current->comm);
+ audit_log_untrustedstring(ab, comm);
audit_log_lsm_data(ab, a);
}
--
2.54.0
^ permalink raw reply related
* [PATCH v3 0/7] sched: Add support for long task name
From: André Almeida @ 2026-06-12 16:20 UTC (permalink / raw)
To: Peter Zijlstra, Juri Lelli, Vincent Guittot, Steven Rostedt,
Christian Brauner, Kees Cook, Shuah Khan, willy,
mathieu.desnoyers, David Laight, Linus Torvalds, akpm,
Yafang Shao, andrii.nakryiko, arnaldo.melo, Petr Mladek
Cc: linux-kernel, kernel-dev, linux-mm, linux-api, André Almeida
* Use case
When debugging and tracing complex programs with hundreds of threads, 16 bytes
long thread names are not enough anymore. cmd_line can show a lot of
characters, but it's not affected by pthread_setname_np() or
prctl(PR_SET_NAME), so let's give the same love kthreads got with commit
6b59808bfe48 ("workqueue: Show the latest workqueue name in
/proc/PID/{comm,stat,status}"). This work creates a new
PR_{SET,GET}_EXT_NAME that supports 64 bytes long names.
It also introduces a new function copy_task_comm() that ensures that the string
is always NUL-terminated despite of mismatching sizes of buffers. We can't just
use strscpy() because it proved to give some overhead[0] in tracing.
* Patchset
Patch 1 is just a minor comment update.
Patch 2 and 3 do some prep work in order to avoid buffer overflows around
the kernel, now that current->comm is bigger. It also make sure that if
the destination buffer is smaller than TASK_COMM_EXT_LEN, it will
be NUL-terminated.
Patch 4 adds a KUnit for the new function copy_task_comm()
Patch 5 sets current->comm length to TASK_COMM_EXT_LEN and take care of
making sure that current userspace APIs gets only TASK_COMM_LEN.
Patch 6 creates new prctl() to set and get all the TASK_COMM_EXT_LEN bytes.
Patch 7 adapts the existing selftest for this new interface.
* Testing
selftests/prctl/set-process-name.c survives this patchset, and it was extended
to the new interface. KUnit test was modified to support copy_task_comm().
I ran the same benchmark as at [0], and the result shows a increase of 0.7% of
overhead when running `perf stat -r 100 hackbench` with sched trace events
enabled (`trace-cmd start -e sched`):
Without this patchset:
213,745,340 cycles:u # 0.119 GHz ( +- 0.33% )
After:
215,111,672 cycles:u # 0.119 GHz ( +- 0.36% )
* Changes
Since v2:
- Add a custom function copy_task_comm() that uses memcpy when possible and
fallback to strscpy(). It always ensures that the string in NUL-terminated
- Add KUnit test for the new function
- Link to v2: https://patch.msgid.link/20260524-tonyk-long_name-v2-0-332f6bd041c4@igalia.com
Since v1:
- Replace new strtostr() with strscpy()
- Don't replace memcpy in tools/
- Link to v1: https://patch.msgid.link/20260517-tonyk-long_name-v1-0-3c282eaa91e2@igalia.com
[0] https://lore.kernel.org/lkml/20260526190625.3f4aca0a@gandalf.local.home/
---
André Almeida (7):
sched: Update get_task_comm() comment
treewide: Get rid of get_task_comm()
treewide: Replace memcpy(..., current->comm) with copy_task_comm()
lib/string_kunit: Add test for copy_task_comm()
sched: Extend task command name with TASK_COMM_EXT_LEN
prctl: Add support for long user thread names
selftests: prctl: Add test for long thread names
drivers/connector/cn_proc.c | 2 +-
drivers/dma-buf/sw_sync.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 2 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 4 +--
drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 2 +-
drivers/gpu/drm/lima/lima_ctx.c | 2 +-
drivers/gpu/drm/panfrost/panfrost_gem.c | 2 +-
drivers/gpu/drm/panthor/panthor_gem.c | 2 +-
drivers/gpu/drm/panthor/panthor_sched.c | 2 +-
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 2 +-
drivers/hwtracing/stm/core.c | 2 +-
drivers/tty/tty_audit.c | 2 +-
fs/binfmt_elf.c | 2 +-
fs/binfmt_elf_fdpic.c | 2 +-
fs/proc/array.c | 2 +-
include/linux/coredump.h | 2 +-
include/linux/sched.h | 35 ++++++++++----------
include/linux/tracepoint.h | 4 +--
include/trace/events/block.h | 10 +++---
include/trace/events/coredump.h | 2 +-
include/trace/events/f2fs.h | 4 +--
include/trace/events/oom.h | 2 +-
include/trace/events/osnoise.h | 2 +-
include/trace/events/sched.h | 10 +++---
include/trace/events/signal.h | 2 +-
include/trace/events/task.h | 7 ++--
include/uapi/linux/prctl.h | 3 ++
kernel/audit.c | 6 ++--
kernel/auditsc.c | 6 ++--
kernel/printk/nbcon.c | 2 +-
kernel/printk/printk.c | 4 +--
kernel/sys.c | 23 ++++++++++---
lib/tests/string_kunit.c | 38 ++++++++++++++++++++++
net/bluetooth/hci_sock.c | 2 +-
net/netfilter/nf_tables_api.c | 4 ++-
security/integrity/integrity_audit.c | 3 +-
security/ipe/audit.c | 3 +-
security/landlock/domain.c | 2 +-
security/lsm_audit.c | 7 ++--
tools/testing/selftests/prctl/set-process-name.c | 36 ++++++++++++++++++++
43 files changed, 178 insertions(+), 79 deletions(-)
---
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
change-id: 20260516-tonyk-long_name-b9f345aeb041
Best regards,
--
André Almeida <andrealmeid@igalia.com>
^ permalink raw reply
* [PATCH v3 1/7] sched: Update get_task_comm() comment
From: André Almeida @ 2026-06-12 16:20 UTC (permalink / raw)
To: Peter Zijlstra, Juri Lelli, Vincent Guittot, Steven Rostedt,
Christian Brauner, Kees Cook, Shuah Khan, willy,
mathieu.desnoyers, David Laight, Linus Torvalds, akpm,
Yafang Shao, andrii.nakryiko, arnaldo.melo, Petr Mladek
Cc: linux-kernel, kernel-dev, linux-mm, linux-api, André Almeida
In-Reply-To: <20260612-tonyk-long_name-v3-0-7989b66e8a99@igalia.com>
Since commit 3a3f61ce5e0b ("exec: Make sure task->comm is always
NUL-terminated"), __set_task_comm() no longer uses strscpy_pad(). Update
the stale comment accordingly.
Signed-off-by: André Almeida <andrealmeid@igalia.com>
---
include/linux/sched.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 368c7b4d7cb5..60d004a49a27 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2005,7 +2005,7 @@ extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec
* User space can randomly change their names anyway, so locking for readers
* doesn't make sense. For writers, locking is probably necessary, as a race
* condition could lead to long-term mixed results.
- * The strscpy_pad() in __set_task_comm() can ensure that the task comm is
+ * The logic inside __set_task_comm() ensures that the task comm is
* always NUL-terminated and zero-padded. Therefore the race condition between
* reader and writer is not an issue.
*
--
2.54.0
^ permalink raw reply related
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: John Ericson @ 2026-06-11 18:53 UTC (permalink / raw)
To: Mateusz Guzik, Li Chen
Cc: Andy Lutomirski, Christian Brauner, Kees Cook, Al Viro,
linux-fsdevel, linux-api, LKML, linux-mm, linux-arch, linux-doc,
linux-kselftest, x86, Arnd Bergmann, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <hd3i6pxxohsjesyid7nhuic6ppp6nyoxxpwa4mny6riqvpyqec@mylfprni2yaw>
On Wed, Jun 10, 2026, at 7:40 PM, Mateusz Guzik wrote:
> [...]
>
> As I tried to explain in my previous e-mail this approach does not cut
> it because of NUMA.
>
> Suppose you have a machine with 2 nodes. The parent-to-be is running
> on node 0 and the child is intended to exec something on node 1.
>
> When the parent-to-be allocates and populates stuff, it takes place with
> memory backed by node 0. If you allocate task_struct, the file table and
> other frequently used (and modified!) objs in this way, you are
> guaranteeing performance loss due to interconnect traffic to access it.
>
> Trying to add plumbing so that all allocations respect numa placement is
> probably too cumbersome.
Are we sure that last part is true?
Let's also assume when this stuff was initially implemented, we didn't
have it. If the basic thrust of this work is to replace functions that
previously only worked on the current thread with those that worked on
either arbitrary (not yet started) threads or the current thread, would
that not prepare us for slowly migrating the allocation choice to
reflect the node of the target task (new parameter) rather than the node
of the current task over time?
(This assumes the task is pre-placed on a node before it is actually run
there, and that pre-placement happens as early in the allocation process
as possible, so subsequent allocations can read off the
partially-initialized task's node.)
"Slowly migrating" is good here! It doesn't need to be the fastest thing
out of the gate, but if this new proper spawning API gets popular as I
think it would, and there is a clear path to optimizing it per the
above, then I am confident that over the years it will happen.
> The primary example for that is looking up the binary to exec in the
> first place.
>
> userspace likes to pass paths which don't exist, meaning checking for
> the binary before any hard work is a useful optimization. Suppose the
> binary to be executed is in a container bound with a taskset using
> node 1 and the content of the fs part of the container is currently
> fully uncached.
>
> When you perform the lookup on node 0, you are populating a bunch of
> metadata (inode, dentry) using memory from that domain. But the intended
> user will only execute on node 1, again resulting in a performance loss.
>
> In order to not do it you would need to convince VFS to allocate memory
> elsewhere.
One thing I don't get about this is that isn't the cost doing a bunch of
work searching the PATH for the directories where the executable
*doesn't* exist? In the case of something like a shell that is going to
spawn a lot of processes, I would think it is *good* to keep all that
PATH crawling VFS filling to be on the shell's node, rather than the
child processes' nodes.
It is only the executable itself, the final step of the VFS crawl, that
should be loaded into the other NUMA nodes. Insofar as (unless I am
missing something) creating the process means finding the inode for the
executable but not loading those pages, aren't we OK here? Only when the
new process is actually scheduled and run must the ELF be paged into
memory, and then that will happen on the correct node.
> So I stand by my previous claim that ultimately a pristine child has to
> be created (like in this patch), but which also has to do the work on
> its own.
I have not been a kernel dev, so my apologies if I am missing things.
But in conclusion for me, the FS and other resource access patterns of
*creating a process* vs *that process itself running* do not seem
necessarily coincident to me. What you are describing as for sure a
problem might possibly be a *good thing*, if they are in fact quite
different.
> Suppose there is no explicit placement requested anywhere. Even in that
> case there are legitimate workloads which will eventually be forced to
> exec stuff on another node. Even these have a better chance retaining
> full locality if the child process does all the work.
>
> Per my previous message I don't see a clean interface to do it.
> something quasi-posix_spawn is probably the least bad way out, it will
> also allow userspace to easily wrap the new thing with posix_spawn
> itself.
>
> Also note there is another issue with the fd-based approach: the fd will
> get inherited on fork and will hang out in the child afterwards unless
> explicitly closed. Suppose you have a multithreaded program which likes
> to both fork(+no exec) and fork+exec. With the fd-based approach you
> have no means of stopping another thread from grabbing your state thanks
> to unix defaulting to copying everything. There was an attempt to fix
> this aspect with O_CLOFORK, but this got rejected.
I would think we don't need to worry about clone/fork very much, right?
I think the premise of your emails, and just about everyone else's in
this thread too, is that we agree fork+exec is bad, and the problem of
unnecessarily sharing resources is inherent to fork. Furthermore, I
think we all agree that while `O_CLOEXEC` and `O_CLOFORK` may help, both
are unsatisfying solutions because they are opt-out not opt-in, and
global to the parent process / preexec state (respectively) rather than
local to the specific fork / exec in question.
pidfds encounter these problems no more than any other
file-descriptor-based UAPI, right? And I don't think it is good to blame
any such file-descriptor-based UAPI when fork/exec are at fault.
Maybe during the transition, when some things use fork and some things
use this new API, stuff will be awkward, but I would rather that just be
an incentive to complete the transition away from fork, not a reason to
second-guess the plan.
Once the transition is complete, and everyone is diligently assembling
their child processes from scratch as is proposed, `O_CLOEXEC` and
`O_CLOFORK` are both unneeded, and oversharing privileges will be much
less common simply because "lazy coding"/"minimal typing" will only
share what is needed --- anything else is more code/keystrokes!
> Whatever exactly happens, NUMA is a sad fact of computing and needs to
> be accounted for. The approach as proposed not only does not do it, but
> it actively hinders such deployments.
Despite everything I said, I want to be clear that I do agree that NUMA
performance should be accounted for. Even if the first version isn't as
great as it could be on that metric, there should be a clear plan for
how future work can conclusively address it.
Cheers,
John
^ permalink raw reply
* [linux-next:master] [vmsplice] e2c0b23680: stress-ng.vm-splice.vm-splice_calls_per_sec 10.2% regression
From: kernel test robot @ 2026-06-11 6:23 UTC (permalink / raw)
To: Askar Safin
Cc: oe-lkp, lkp, Christian Brauner, linux-fsdevel, netdev,
linux-kernel, linux-api, oliver.sang
Hello,
kernel test robot noticed a 10.2% regression of stress-ng.vm-splice.vm-splice_calls_per_sec on:
commit: e2c0b2368081bef7d1f6758cc9e7edde8521237c ("vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2")
https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git master
[still regression on linux-next/master 6e845bcb78c95af935094040bd4edc3c2b6dd784]
testcase: stress-ng
config: x86_64-rhel-9.4
compiler: gcc-14
test machine: 192 threads 2 sockets Intel(R) Xeon(R) 6740E CPU @ 2.4GHz (Sierra Forest) with 256G memory
parameters:
nr_threads: 100%
testtime: 60s
test: vm-splice
cpufreq_governor: performance
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202606111018.cc992614-lkp@intel.com
Details are as below:
-------------------------------------------------------------------------------------------------->
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20260611/202606111018.cc992614-lkp@intel.com
=========================================================================================
compiler/cpufreq_governor/kconfig/nr_threads/rootfs/tbox_group/test/testcase/testtime:
gcc-14/performance/x86_64-rhel-9.4/100%/debian-13-x86_64-20250902.cgz/lkp-srf-2sp3/vm-splice/stress-ng/60s
commit:
a9f7db50ed ("tee: fs/splice.c: remove unused parameter "flags" from "link_pipe"")
e2c0b23680 ("vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2")
a9f7db50ed2fff96 e2c0b2368081bef7d1f6758cc9e
---------------- ---------------------------
%stddev %change %stddev
\ | \
2122 -2.7% 2064 stress-ng.time.user_time
13281641 -10.2% 11928183 stress-ng.vm-splice.MB_per_sec_vm-splice_rate
1.183e+10 -4.8% 1.126e+10 stress-ng.vm-splice.ops
1.972e+08 -4.8% 1.878e+08 stress-ng.vm-splice.ops_per_sec
3.4e+09 -10.2% 3.054e+09 stress-ng.vm-splice.vm-splice_calls_per_sec
1397 ± 7% -21.1% 1103 ± 10% sched_debug.cfs_rq:/.runnable_avg.max
1.28 -22.1% 1.00 ± 44% turbostat.IPC
74419 +3.9% 77317 proc-vmstat.nr_shmem
633806 +1.1% 640727 proc-vmstat.numa_local
331878 ± 13% -30.3% 231247 ± 18% numa-numastat.node0.local_node
37379 ± 76% +241.6% 127705 ± 50% numa-numastat.node0.other_node
298829 ± 14% +36.1% 406730 ± 10% numa-numastat.node1.local_node
162072 ± 17% -55.7% 71783 ± 89% numa-numastat.node1.other_node
332129 ± 13% -30.4% 231269 ± 18% numa-vmstat.node0.numa_local
37379 ± 76% +241.6% 127705 ± 50% numa-vmstat.node0.numa_other
298881 ± 14% +36.1% 406833 ± 10% numa-vmstat.node1.numa_local
162072 ± 17% -55.7% 71783 ± 89% numa-vmstat.node1.numa_other
Disclaimer:
Results have been estimated based on internal Intel analysis and are provided
for informational purposes only. Any difference in system hardware or software
design or configuration may affect actual performance.
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Mateusz Guzik @ 2026-06-10 23:40 UTC (permalink / raw)
To: Li Chen
Cc: John Ericson, Andy Lutomirski, Christian Brauner, Kees Cook,
Al Viro, linux-fsdevel, linux-api, LKML, linux-mm, linux-arch,
linux-doc, linux-kselftest, x86, Arnd Bergmann, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, H. Peter Anvin,
Jan Kara, Jonathan Corbet, Shuah Khan
In-Reply-To: <19eb181fdd4.6d028f442844776.3737831021032223216@linux.beauty>
On Wed, Jun 10, 2026 at 08:29:06PM +0800, Li Chen wrote:
> ---- On Wed, 10 Jun 2026 01:27:47 +0800 John Ericson <mail@johnericson.me> wrote ---
> > Hope the above answers your question? I suppose my ideas lean more on the
> > "future" than "empty" side --- there is indeed a thread in the thread group,
> > with real VM/namespace/file descriptor etc. state. Moreover, state gets
> > initialized before the process is started, so the actual start is a pretty
> > lightweight step of just letting the scheduler know the now-ready process can
> > be scheduled. The only thing that distinguishes the embryonic process from a
> > real one is simply that it isn't running --- i.e. isn't (yet) available to be
> > scheduled --- so the pidfds holders are free to poke at its state.
> >
>
> Thanks, this helped a lot. I looked at FreeBSD/OpenBSD/XNU after your
> note. FreeBSD has P_INEXEC, OpenBSD has PS_INEXEC, and XNU seems even
> closer with P_LINTRANSIT, described as "process in exec or in creation".
> Linux does not seem to have a single equivalent today: current->in_execve
> is only an LSM hint, while the real synchronization is spread across
> exec_update_lock, cred_guard_mutex, and the exec path.
>
> I am switching my local WIP from the two-fd builder model to one fd,
> closer to Christian's sketch:
>
> fd = pidfd_open(0, PIDFD_EMPTY);
> pidfd_config(fd, ...);
> pidfd_spawn_run(fd, ...);
>
> In my current local version, I still use copy_process(), so the fd points
> at a real task_struct/pid that is not woken until run. Following
> Christian's point that existing APIs can handle this not-yet-running case
> with ESRCH, I currently make ordinary pidfd operations that need a real
> started process return -ESRCH before start.
>
> I am not sure yet whether Linux should grow a general exec/creation
> transition state like that, or whether a narrower future-process
> lifecycle is enough for this API. I will think more about that when
> working on the pristine process version.
>
As I tried to explain in my previous e-mail this approach does not cut
it because of NUMA.
Suppose you have a machine with 2 nodes. The parent-to-be is running
on node 0 and the child is intended to exec something on node 1.
When the parent-to-be allocates and populates stuff, it takes place with
memory backed by node 0. If you allocate task_struct, the file table and
other frequently used (and modified!) objs in this way, you are
guaranteeing performance loss due to interconnect traffic to access it.
Trying to add plumbing so that all allocations respect numa placement is
probably too cumbersome.
The primary example for that is looking up the binary to exec in the
first place.
userspace likes to pass paths which don't exist, meaning checking for
the binary before any hard work is a useful optimizaiton. Suppose the
binary to be executed is in a container bound with a taskset using
node 1 and the content of the fs part of the container is currently
fully uncached.
When you perform the lookup on node 0, you are populating a bunch of
metadata (inode, dentry) using memory from that domain. But the intended
user will only execute on node 1, again resulting in a performance loss.
In order to not do it you would need to convince VFS to allocate memory
elsewhere.
So I stand by my previous claim that ultimately a pristine child has to
be created (like in this patch), but which also has to do the work on
its own.
Suppose there is no explicit placement requested anywhere. Even in that
case there are legitimate workloads which will eventually be forced to
exec stuff on another node. Even these have a better chance retaining
full locality if the child process does all the work.
Per my previous message I don't see a clean interface to do it.
something quasi-posix_spawn is probably the least bad way out, it will
also allow userspace to easily wrap the new thing with posix_spawn
itself.
Also note there is another issue with the fd-based approach: the fd will
get inherited on fork and will hang out in the child afterwards unless
explicitly closed. Suppose you have a multithreaded program which likes
to both fork(+no exec) and fork+exec. With the fd-based approach you
have no means of stopping another thread from grabbing your state thanks
to unix defaulting to copying everything. There was an attempt to fix
this aspect with O_CLOFORK, but this got rejected.
Whatever exactly happens, NUMA is a sad fact of computing and needs to
be accounted for. The approach as proposed not only does not do it, but
it actively hinders such deployments.
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Mateusz Guzik @ 2026-06-10 22:59 UTC (permalink / raw)
To: Jann Horn
Cc: Christian Brauner, Li Chen, Kees Cook, Alexander Viro,
linux-fsdevel, linux-api, linux-kernel, linux-mm, linux-arch,
linux-doc, linux-kselftest, x86, Arnd Bergmann, Andy Lutomirski,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
H. Peter Anvin, Jan Kara, Jonathan Corbet, Shuah Khan
In-Reply-To: <CAG48ez38OEE8ZPLyU6nr9=cYx-hMsdoh5WRrv-GMZGMDKyyOTA@mail.gmail.com>
On Mon, Jun 8, 2026 at 5:02 PM Jann Horn <jannh@google.com> wrote:
>
> On Thu, May 28, 2026 at 2:55 PM Mateusz Guzik <mjguzik@gmail.com> wrote:
> > This problem is dear to my heart and I have been pondering it on and off
> > for some time now. The entire fork + exec idiom is terrible and needs to
> > be retired.
>
> It seems to me like vfork+exec is a decent UAPI building block, on
> which you can build nice-looking userspace APIs, though I agree that
> this is not an ideal direct interface for application code.
>
> > Additionally there is a known problem where transiently copied file
> > descriptors on fork + exec cause a headache in multithreaded programs
> > doing something like this in parallel. I only did cursory reading, it
> > seems your patchset keeps the same problem in place.
>
> I think we almost have UAPI that would let you avoid this issue?
> You can use clone() with CLONE_FILES, then unshare the FD table with
> close_range(3, UINT_MAX, CLOSE_RANGE_UNSHARE). That is not currently
> implemented to be atomic with stuff that happens on other threads, but
> if we changed that, and it doesn't provide a good way to carry some
> FDs across, but it feels to me like this could be fixed with a variant
> of close_range() that removes O_CLOEXEC FDs except ones listed in an
> array.
Suppose you want to exec a binary with the following fd set:
0 is /dev/null
1 is fd 1023 in your process
2 is fd 1023 in your process
You have tons of other fds and you don't want any of them anywhere near this.
Clean interface from my standpoint would avoid any unnecessary
overhead and would allow you to clearly specify what do you want.
In this case whatever the interface it should provide the ability to
map 1023 to 1 and 2 in the child. With the current syscall set you get
refs taken on these on clone, then you have to manually dup2 these
which is separate syscalls with extra atomics on top. A fast & elegant
solution would allow you to tell the kernel directly where to install
the 2 files.
Also note in practical terms userspace likes to closefrom/close_range
anyway to get rid of unwanted fds which happen to not have the cloexec
bit which is yet another syscall to invoke on the way to exec. A
better interface would instantly avoid the problem by not copying the
unwanted fds if not asked. For viability for use as foundation to
build posix_spawn over it such copying would have to be supported of
course.
>
> > There are numerous impactful ways to speed up execs both in terms of
> > single-threaded cost and their multicore scalability, most of which
> > would be immediately usable by all programs without an opt-in. imo these
> > needs to be exhausted before something like a "template" can be
> > considered.
>
> (I think probably a large part of this would be stuff that happens in
> userspace, like dynamic linking.)
I have not investigated userspace, even putting specific APIs aside
the kernel has *a lot* of avoidable overhead.
>
> > Per the above, the primary win would stem from *NOT* messing with mm.
>
> As you write below, I think we have that with CLONE_MM? The C function
> vfork() is kind of a terrible API because of its returns-twice
> behavior, but I think if process cloning with CLONE_VM|CLONE_VFORK was
> wrapped by libc in a way similar to clone() (with the child executing
> a separate handler function), or if it was used in the implementation
> of some higher-level process-spawning API, it would be a perfectly
> fine API?
>
> Or am I misunderstanding what you mean by "messing with mm"?
>
I was not aware of this functionality, let's assume it indeed works.
You still have the file issue described above.
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: John Ericson @ 2026-06-10 20:38 UTC (permalink / raw)
To: Li Chen
Cc: Andy Lutomirski, Christian Brauner, Kees Cook, Al Viro,
linux-fsdevel, linux-api, LKML, linux-mm, linux-arch, linux-doc,
linux-kselftest, x86, Arnd Bergmann, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <19eb181fdd4.6d028f442844776.3737831021032223216@linux.beauty>
On Wed, Jun 10, 2026, at 8:29 AM, Li Chen wrote:
> Hi John,
>
> [...]
>
> Thanks, this helped a lot. I looked at FreeBSD/OpenBSD/XNU after your
> note. FreeBSD has P_INEXEC, OpenBSD has PS_INEXEC, and XNU seems even
> closer with P_LINTRANSIT, described as "process in exec or in creation".
> Linux does not seem to have a single equivalent today: current->in_execve
> is only an LSM hint, while the real synchronization is spread across
> exec_update_lock, cred_guard_mutex, and the exec path.
Great! Glad to hear my suggestion (and the patch too I linked in the
other email, I hope?) was useful.
> I am switching my local WIP from the two-fd builder model to one fd,
> closer to Christian's sketch:
>
> fd = pidfd_open(0, PIDFD_EMPTY);
> pidfd_config(fd, ...);
> pidfd_spawn_run(fd, ...);
Glad to hear it is also one-fd now.
> In my current local version, I still use copy_process(), so the fd points
> at a real task_struct/pid that is not woken until run.
So this is an interesting thing to think about. My hunch is that
`copy_process` is, at least in the longer term, still doing too much! In
particular, `struct kernel_clone_args` has many degrees of freedom, and
might also make assumptions about preserving more of the parent process
than is needed in this case.
This is a bit tangential, but one thing I have thought about is having
"null namespaces". I think the current (i.e. existing clone API) default
of "share with parent process" is a poor security practice (more
privileges, i.e. sharing, should always be opt-in). But the opposite
default of "unshare everything" is expensive since creating new
namespaces is non-free. The goal of the null namespaces would be a cheap
way of creating a more isolated and unprivileged process — and "cheap"
here is literal: a null pointer in `nsproxy`, no allocation, no
namespace object, no ID. This null state would be what
`pidfd_open(0, PIDFD_EMPTY)` (using your example above, or really
whatever the first step is) hands back.
Then, from that maximally cheap and unprivileged initial state, the
`pidfd_config(fd, ...);` calls (plural important, I think!) would opt
into either sharing or unsharing namespaces between the child and parent
as the parent sees fit.
The larger point here is that insofar as there are not good defaults for
things, there is pressure, whether in step 1 or step 2, to make larger
everything-at-once configuration. But when we think a bit outside the
box to create the good defaults where they didn't previously exist, we
can end up in a situation where a minimal initial blank unstarted
process, and the builder pattern to initialize it, are more "natural".
> Following
> Christian's point that existing APIs can handle this not-yet-running case
> with ESRCH, I currently make ordinary pidfd operations that need a real
> started process return -ESRCH before start.
Also glad to hear.
> I am not sure yet whether Linux should grow a general exec/creation
> transition state like that, or whether a narrower future-process
> lifecycle is enough for this API. I will think more about that when
> working on the pristine process version.
Sounds good, as I think you can guess, my preference is for "yes", but I
agree we can see what you end up with in the next patchset and make more
informed decisions based on that.
Cheers,
John
^ permalink raw reply
* Re: [PATCH 0/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: Linus Torvalds @ 2026-06-10 14:17 UTC (permalink / raw)
To: Herbert Xu
Cc: metze, luto, safinaskar, akpm, axboe, brauner, david, dhowells,
hch, jack, linux-api, linux-fsdevel, linux-kernel, linux-mm,
miklos, netdev, patches, pfalcato, viro, willy
In-Reply-To: <aijyuX14ahOf53zM@gondor.apana.org.au>
On Tue, 9 Jun 2026 at 22:14, Herbert Xu <herbert@gondor.apana.org.au> wrote:
>
> How about using write access as the gate to zero copy?
Well, it's too late now - no sane user would open files for writing
just to read from them.
And if the file isn't opened for writing originally, it won't show up
in the fd. So we'd have to do some after-the-fact "is this writable
using the credentials we had at open time" thing.
Which we do for some /proc stuff etc, but it's disgusting, and it
isn't how any of this should work.
Plus honestly, in any *good* setup, the normal case really shouldn't
be that the source is writable anyway. In benchmarks, sure. But in
real life? Hell no.
If you're doing web serving of static content, your web server simply
should not have write access to what it is serving. If it does, your
web server setup is simply hot garbage and probably riddled with
security issues anyway, because you clearly spent absolutely zero time
on any kind of "minimal privileges" model.
(I'm not saying that splice would be primarily for web serving or that
serving static pages like that is an interesting load - I'm using it
purely as an example of why saying that writable files should get
better treatment is crazy: it's not how *any* of this should work at
any level at all).
So no. We should not treat writable files as some kind of preferred
model for performance. We should discourage that kind of behavior, not
encourage it.
Linus
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Li Chen @ 2026-06-10 12:29 UTC (permalink / raw)
To: John Ericson
Cc: Andy Lutomirski, Christian Brauner, Kees Cook, Al Viro,
linux-fsdevel, linux-api, LKML, linux-mm, linux-arch, linux-doc,
linux-kselftest, x86, Arnd Bergmann, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <4e049396-377d-48a7-a34c-91318413a876@app.fastmail.com>
Hi John,
---- On Wed, 10 Jun 2026 01:27:47 +0800 John Ericson <mail@johnericson.me> wrote ---
>
>
> On Tue, Jun 9, 2026, at 10:43 AM, Li Chen wrote:
> > Hi Andy,
> >
> > ---- On Tue, 09 Jun 2026 08:01:57 +0800 Andy Lutomirski <luto@kernel.org> wrote ---
> > > [...]
> > >
> > > After contemplating this for a bit... why pidfd? Doesn't a pidfd
> > > refer to an actual process that is, or at least was, running? This
> > > new thing is a process that we are contemplating spawning. I can
> > > imagine that basically all pidfd APIs would be a bit confused by the
> > > nonexistence of the process in question.
> > >
> >
> > Yes, I think that is a real concern.
> >
> > In my current local WIP I tried to keep that distinction explicit.
> > pidfd_spawn_open() returns a pidfs-backed builder fd, not a normal pidfd
> > referring to a process. The builder fd is allocated as an anonymous pidfs
> > file with builder-specific file operations:
> >
> > file = pidfs_alloc_anon_file("[pidfd_spawn]",
> > &pidfd_spawn_builder_fops, builder,
> > O_RDWR);
> >
>
> What does your builder fd point to, explicitly? For example in my other reply I
> talked about how it was "real" process state. In my FreeBSD patch, for example,
> I found there was already a status for a process "in exec", and I figured that
> was clean to reuse for one of these "embryonic" processes that also hadn't
> started running. I would reckon that Linux probably has some similar notions.
>
> > and the normal pidfd helpers still reject it because it does not use the
> > ordinary pidfd file operations:
> >
> > struct pid *pidfd_pid(const struct file *file)
> > {
> > if (file->f_op != &pidfs_file_operations)
> > return ERR_PTR(-EBADF);
> > return file_inode(file)->i_private;
> > }
> >
> > So the current split is:
> >
> > builder_fd = pidfd_spawn_open(...); /* builder object */
> > pidfd_config(builder_fd, ...);
> > child_pidfd = pidfd_spawn_run(builder_fd, ...); /* real pidfd */
> >
> > Only the last fd is a normal pidfd for an actual child process. The builder
> > fd is only accepted by the builder operations.
> >
> > This avoids having to define what waitid(P_PIDFD), pidfd_send_signal(),
> > pidfd_getfd(), poll(), etc. mean before the process exists.
>
> I wouldn't be so sure this is necessary/good. For example, I think it could
> make sense to wait on a process that has yet to be started; one just waits for
> both the process to start and the process to exit. Obviously a blocking syscall
> in the thread that is spawning the process is not useful, but the asynchronous
> poll variation seems fine.
>
> As long as there is real process state here, it shouldn't be too hard to
> implement.
>
> > The downside is that it adds a separate open-style entry point and is less
> > uniform than the pidfd_open(0, PIDFD_EMPTY) spelling Christian sketched.
>
> I do think there is no point having two file descriptors. The file descriptor
> that previously referred to the builder/embryonic process then can refer to the
> real process, right?
>
> > If people think there is a better way to represent the pre-spawn builder
> > state, or if the preference is to integrate it directly into pidfd_open()
> > with an explicit empty/future-pidfd state, I would be happy to discuss that.
>
> Hope the above answers your question? I suppose my ideas lean more on the
> "future" than "empty" side --- there is indeed a thread in the thread group,
> with real VM/namespace/file descriptor etc. state. Moreover, state gets
> initialized before the process is started, so the actual start is a pretty
> lightweight step of just letting the scheduler know the now-ready process can
> be scheduled. The only thing that distinguishes the embryonic process from a
> real one is simply that it isn't running --- i.e. isn't (yet) available to be
> scheduled --- so the pidfds holders are free to poke at its state.
>
> Cheers,
>
> John
>
Thanks, this helped a lot. I looked at FreeBSD/OpenBSD/XNU after your
note. FreeBSD has P_INEXEC, OpenBSD has PS_INEXEC, and XNU seems even
closer with P_LINTRANSIT, described as "process in exec or in creation".
Linux does not seem to have a single equivalent today: current->in_execve
is only an LSM hint, while the real synchronization is spread across
exec_update_lock, cred_guard_mutex, and the exec path.
I am switching my local WIP from the two-fd builder model to one fd,
closer to Christian's sketch:
fd = pidfd_open(0, PIDFD_EMPTY);
pidfd_config(fd, ...);
pidfd_spawn_run(fd, ...);
In my current local version, I still use copy_process(), so the fd points
at a real task_struct/pid that is not woken until run. Following
Christian's point that existing APIs can handle this not-yet-running case
with ESRCH, I currently make ordinary pidfd operations that need a real
started process return -ESRCH before start.
I am not sure yet whether Linux should grow a general exec/creation
transition state like that, or whether a narrower future-process
lifecycle is enough for this API. I will think more about that when
working on the pristine process version.
Regards,
Li
^ permalink raw reply
* Re: [PATCH 0/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: David Laight @ 2026-06-10 9:55 UTC (permalink / raw)
To: Herbert Xu
Cc: Linus Torvalds, luto, safinaskar, akpm, axboe, brauner, david,
dhowells, hch, jack, linux-api, linux-fsdevel, linux-kernel,
linux-mm, miklos, netdev, patches, pfalcato, viro, willy
In-Reply-To: <aijxfxXrWLCVqV6-@gondor.apana.org.au>
On Wed, 10 Jun 2026 13:09:19 +0800
Herbert Xu <herbert@gondor.apana.org.au> wrote:
> Linus Torvalds <torvalds@linux-foundation.org> wrote:
> >
> > Because I think splice() is a *cool* feature. It was always *clever*.
> > I just don't think it's worth the pain it has cause.
> >
> > And it's been around for a long long time, and after more than two
> > decades it's still most definitely not _widely_ used.
>
> A couple of years ago I used tee(2) in dash(1) so that we could
> avoid reading the input line byte-by-byte which is what every other
> shell does in order to pass the rest of stdin to the executed
> command.
The shell just needs something like MSG_PEEK to do a non-consuming
read from a pipe.
(Without the strange behaviour of a second offset.)
That would be simple and could have been implemented 40 years ago.
-- David
>
> https://git.kernel.org/pub/scm/utils/dash/dash.git/commit/?id=44b15ea09a9ee5872cf477e4ffc6b42ef37d1e46
>
> It's definitely niche but made a huge performance difference to
> this rather common scenario:
>
> echo 'command
> ...
> rest of stdin' | sh
>
> I didn't even know tee(2) prior to this, even though it was added
> way back.
>
> Thanks,
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Christian Brauner @ 2026-06-10 7:28 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Li Chen, Kees Cook, Alexander Viro, linux-fsdevel, linux-api,
linux-kernel, linux-mm, linux-arch, linux-doc, linux-kselftest,
x86, Arnd Bergmann, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <CALCETrWJQpLR4n1cpichBk8=uExSKLWTMGU3BufGdk_WE_p5UA@mail.gmail.com>
On Mon, Jun 08, 2026 at 05:01:57PM -0700, Andy Lutomirski wrote:
> On Thu, May 28, 2026 at 4:05 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Thu, May 28, 2026 at 05:52:21PM +0800, Li Chen wrote:
> > > Hi,
> > >
> > > This is an early RFC for an idea that is probably still rough in both the
> > > UAPI and implementation details. Sorry for the rough edges; I am sending
> > > it now to check whether this direction is worth pursuing and to get
> > > feedback on the kernel/userspace boundary.
> >
> > The idea of having a builder api for exec isn't all that crazy. But it
> > should simply be built on top of pidfds and thus pidfs itself instead.
> > It has all the basic infrastructure in place already. Any implementation
> > should also allow userspace to implement posix_spawn() on top of it.
> >
> > fd = pidfd_open(0, PIDFD_EMPTY /* or better name */)
> >
> > pidfd_config(fd, ...) // modeled similar to fsconfig()
> >
>
> After contemplating this for a bit... why pidfd? Doesn't a pidfd
> refer to an actual process that is, or at least was, running? This
> new thing is a process that we are contemplating spawning. I can
> imagine that basically all pidfd APIs would be a bit confused by the
> nonexistence of the process in question.
I don't think that would be a problem because every api just needs to
handle ESRCH. Ignoring that for a second: the mount api has a builder fd
that is later transformed into a pidfd. Which is easily doable here as
well. My point is that all the infrastructure building blocks already
exist in pidfs.
^ permalink raw reply
* Re: [PATCH 0/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: Herbert Xu @ 2026-06-10 5:14 UTC (permalink / raw)
To: Linus Torvalds
Cc: metze, luto, safinaskar, akpm, axboe, brauner, david, dhowells,
hch, jack, linux-api, linux-fsdevel, linux-kernel, linux-mm,
miklos, netdev, patches, pfalcato, viro, willy
In-Reply-To: <CAHk-=whL0oLvmh3FO9SapDhgrXMT_d6qkP2V3OBjysOWXod2Og@mail.gmail.com>
Linus Torvalds <torvalds@linux-foundation.org> wrote:
>
> Nope. If your web server opens files with write access, I'd be
> extremely surprised.
>
> And if you don't have write access, and you're sending out data from
> files you opened just for reading - the onle sane case - you hit all
> the existing problems with "I can certainly look up pages, but I damn
> well shouldn't pass those pages to the networking code without copying
> them".
How about using write access as the gate to zero copy?
IOW allow zero-copy if you already hold write access, otherwise
perform a copy.
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [PATCH 0/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: Herbert Xu @ 2026-06-10 5:09 UTC (permalink / raw)
To: Linus Torvalds
Cc: luto, safinaskar, akpm, axboe, brauner, david, dhowells, hch,
jack, linux-api, linux-fsdevel, linux-kernel, linux-mm, miklos,
netdev, patches, pfalcato, viro, willy
In-Reply-To: <CAHk-=wh5bFj1a7eaGp9sixDg3UXu7xUGfU=YJo+ckpGxGAyhXQ@mail.gmail.com>
Linus Torvalds <torvalds@linux-foundation.org> wrote:
>
> Because I think splice() is a *cool* feature. It was always *clever*.
> I just don't think it's worth the pain it has cause.
>
> And it's been around for a long long time, and after more than two
> decades it's still most definitely not _widely_ used.
A couple of years ago I used tee(2) in dash(1) so that we could
avoid reading the input line byte-by-byte which is what every other
shell does in order to pass the rest of stdin to the executed
command.
https://git.kernel.org/pub/scm/utils/dash/dash.git/commit/?id=44b15ea09a9ee5872cf477e4ffc6b42ef37d1e46
It's definitely niche but made a huge performance difference to
this rather common scenario:
echo 'command
...
rest of stdin' | sh
I didn't even know tee(2) prior to this, even though it was added
way back.
Thanks,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Jann Horn @ 2026-06-09 17:53 UTC (permalink / raw)
To: Florian Weimer
Cc: Mateusz Guzik, Christian Brauner, Li Chen, Kees Cook,
Alexander Viro, linux-fsdevel, linux-api, linux-kernel, linux-mm,
linux-arch, linux-doc, linux-kselftest, x86, Arnd Bergmann,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <lhubjdk1c1m.fsf@oldenburg.str.redhat.com>
On Tue, Jun 9, 2026 at 8:08 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Jann Horn:
>
> >> Per the above, the primary win would stem from *NOT* messing with mm.
> >
> > As you write below, I think we have that with CLONE_MM? The C function
> > vfork() is kind of a terrible API because of its returns-twice
> > behavior, but I think if process cloning with CLONE_VM|CLONE_VFORK was
> > wrapped by libc in a way similar to clone() (with the child executing
> > a separate handler function), or if it was used in the implementation
> > of some higher-level process-spawning API, it would be a perfectly
> > fine API?
>
> No, there is still a problem with SIGTSTP handling because we cannot
> atomically unmask the signal during execve. We need to unblock SIGTSTP
> before execve in the new process, but this means that it can get
> suspended by SIGTSTP. Consequently, the execve never happens and the
> original process is stuck in vfork:
>
> posix_spawn: parent can get stuck in uninterruptible sleep if child
> receives SIGTSTP early enough
> <https://inbox.sourceware.org/libc-help/2921668c-773e-465d-9480-0abb6f979bf9@www.fastmail.com/>
>
> More on the low-level side, it's difficult to make sure that execve gets
> a consistent snapshot of the environ vector. Both vfork and execve need
> to be async-signal-safe. Any locking or memory allocation (except for
> the stack …) persists in the original process after vfork returns. The
I think that's not entirely accurate; if you call set_robust_list() on
a futex list, then call execve(), the futexes should be released once
the process switches to a new MM, in
begin_new_exec -> exec_mmap -> exec_mm_release -> futex_exec_release
-> futex_cleanup -> exit_robust_list.
So in theory you could use clone() with CLONE_VM and without
CLONE_VFORK, and let the parent either wait for a futex that is
released on exec, or somehow asynchronously check later whether the
futex is still held... probably not the nicest building block but
maybe workable? Though I guess it would fit more nicely if there was a
"munmap() this range on exec" API...
> environ vector can be large, so making a copy on the stack is not ideal.
> It's even harder for getenv/setenv/unsetenv implementations that use
> locking instead of software transactional memory.
Makes sense, that kind of sounds like a pain inherent in being able to
execute from signal handler context...
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: John Ericson @ 2026-06-09 17:27 UTC (permalink / raw)
To: Li Chen, Andy Lutomirski
Cc: Christian Brauner, Kees Cook, Al Viro, linux-fsdevel, linux-api,
LKML, linux-mm, linux-arch, linux-doc, linux-kselftest, x86,
Arnd Bergmann, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <19eacd64508.26b92c022125848.262962729296162879@linux.beauty>
On Tue, Jun 9, 2026, at 10:43 AM, Li Chen wrote:
> Hi Andy,
>
> ---- On Tue, 09 Jun 2026 08:01:57 +0800 Andy Lutomirski <luto@kernel.org> wrote ---
> > [...]
> >
> > After contemplating this for a bit... why pidfd? Doesn't a pidfd
> > refer to an actual process that is, or at least was, running? This
> > new thing is a process that we are contemplating spawning. I can
> > imagine that basically all pidfd APIs would be a bit confused by the
> > nonexistence of the process in question.
> >
>
> Yes, I think that is a real concern.
>
> In my current local WIP I tried to keep that distinction explicit.
> pidfd_spawn_open() returns a pidfs-backed builder fd, not a normal pidfd
> referring to a process. The builder fd is allocated as an anonymous pidfs
> file with builder-specific file operations:
>
> file = pidfs_alloc_anon_file("[pidfd_spawn]",
> &pidfd_spawn_builder_fops, builder,
> O_RDWR);
>
What does your builder fd point to, explicitly? For example in my other reply I
talked about how it was "real" process state. In my FreeBSD patch, for example,
I found there was already a status for a process "in exec", and I figured that
was clean to reuse for one of these "embryonic" processes that also hadn't
started running. I would reckon that Linux probably has some similar notions.
> and the normal pidfd helpers still reject it because it does not use the
> ordinary pidfd file operations:
>
> struct pid *pidfd_pid(const struct file *file)
> {
> if (file->f_op != &pidfs_file_operations)
> return ERR_PTR(-EBADF);
> return file_inode(file)->i_private;
> }
>
> So the current split is:
>
> builder_fd = pidfd_spawn_open(...); /* builder object */
> pidfd_config(builder_fd, ...);
> child_pidfd = pidfd_spawn_run(builder_fd, ...); /* real pidfd */
>
> Only the last fd is a normal pidfd for an actual child process. The builder
> fd is only accepted by the builder operations.
>
> This avoids having to define what waitid(P_PIDFD), pidfd_send_signal(),
> pidfd_getfd(), poll(), etc. mean before the process exists.
I wouldn't be so sure this is necessary/good. For example, I think it could
make sense to wait on a process that has yet to be started; one just waits for
both the process to start and the process to exit. Obviously a blocking syscall
in the thread that is spawning the process is not useful, but the asynchronous
poll variation seems fine.
As long as there is real process state here, it shouldn't be too hard to
implement.
> The downside is that it adds a separate open-style entry point and is less
> uniform than the pidfd_open(0, PIDFD_EMPTY) spelling Christian sketched.
I do think there is no point having two file descriptors. The file descriptor
that previously referred to the builder/embryonic process then can refer to the
real process, right?
> If people think there is a better way to represent the pre-spawn builder
> state, or if the preference is to integrate it directly into pidfd_open()
> with an explicit empty/future-pidfd state, I would be happy to discuss that.
Hope the above answers your question? I suppose my ideas lean more on the
"future" than "empty" side --- there is indeed a thread in the thread group,
with real VM/namespace/file descriptor etc. state. Moreover, state gets
initialized before the process is started, so the actual start is a pretty
lightweight step of just letting the scheduler know the now-ready process can
be scheduled. The only thing that distinguishes the embryonic process from a
real one is simply that it isn't running --- i.e. isn't (yet) available to be
scheduled --- so the pidfds holders are free to poke at its state.
Cheers,
John
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Li Chen @ 2026-06-09 14:43 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Christian Brauner, Kees Cook, Alexander Viro, linux-fsdevel,
linux-api, linux-kernel, linux-mm, linux-arch, linux-doc,
linux-kselftest, x86, Arnd Bergmann, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <CALCETrWJQpLR4n1cpichBk8=uExSKLWTMGU3BufGdk_WE_p5UA@mail.gmail.com>
Hi Andy,
---- On Tue, 09 Jun 2026 08:01:57 +0800 Andy Lutomirski <luto@kernel.org> wrote ---
> On Thu, May 28, 2026 at 4:05 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Thu, May 28, 2026 at 05:52:21PM +0800, Li Chen wrote:
> > > Hi,
> > >
> > > This is an early RFC for an idea that is probably still rough in both the
> > > UAPI and implementation details. Sorry for the rough edges; I am sending
> > > it now to check whether this direction is worth pursuing and to get
> > > feedback on the kernel/userspace boundary.
> >
> > The idea of having a builder api for exec isn't all that crazy. But it
> > should simply be built on top of pidfds and thus pidfs itself instead.
> > It has all the basic infrastructure in place already. Any implementation
> > should also allow userspace to implement posix_spawn() on top of it.
> >
> > fd = pidfd_open(0, PIDFD_EMPTY /* or better name */)
> >
> > pidfd_config(fd, ...) // modeled similar to fsconfig()
> >
>
> After contemplating this for a bit... why pidfd? Doesn't a pidfd
> refer to an actual process that is, or at least was, running? This
> new thing is a process that we are contemplating spawning. I can
> imagine that basically all pidfd APIs would be a bit confused by the
> nonexistence of the process in question.
>
Yes, I think that is a real concern.
In my current local WIP I tried to keep that distinction explicit.
pidfd_spawn_open() returns a pidfs-backed builder fd, not a normal pidfd
referring to a process. The builder fd is allocated as an anonymous pidfs
file with builder-specific file operations:
file = pidfs_alloc_anon_file("[pidfd_spawn]",
&pidfd_spawn_builder_fops, builder,
O_RDWR);
and the normal pidfd helpers still reject it because it does not use the
ordinary pidfd file operations:
struct pid *pidfd_pid(const struct file *file)
{
if (file->f_op != &pidfs_file_operations)
return ERR_PTR(-EBADF);
return file_inode(file)->i_private;
}
So the current split is:
builder_fd = pidfd_spawn_open(...); /* builder object */
pidfd_config(builder_fd, ...);
child_pidfd = pidfd_spawn_run(builder_fd, ...); /* real pidfd */
Only the last fd is a normal pidfd for an actual child process. The
builder fd is only accepted by the builder operations.
This avoids having to define what waitid(P_PIDFD), pidfd_send_signal(),
pidfd_getfd(), poll(), etc. mean before the process exists. The downside
is that it adds a separate open-style entry point and is less uniform than
the pidfd_open(0, PIDFD_EMPTY) spelling Christian sketched.
If people think there is a better way to represent the pre-spawn builder
state, or if the preference is to integrate it directly into pidfd_open()
with an explicit empty/future-pidfd state, I would be happy to discuss
that.
Regards,
Li
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Florian Weimer @ 2026-06-09 6:08 UTC (permalink / raw)
To: Jann Horn
Cc: Mateusz Guzik, Christian Brauner, Li Chen, Kees Cook,
Alexander Viro, linux-fsdevel, linux-api, linux-kernel, linux-mm,
linux-arch, linux-doc, linux-kselftest, x86, Arnd Bergmann,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <CAG48ez38OEE8ZPLyU6nr9=cYx-hMsdoh5WRrv-GMZGMDKyyOTA@mail.gmail.com>
* Jann Horn:
>> Per the above, the primary win would stem from *NOT* messing with mm.
>
> As you write below, I think we have that with CLONE_MM? The C function
> vfork() is kind of a terrible API because of its returns-twice
> behavior, but I think if process cloning with CLONE_VM|CLONE_VFORK was
> wrapped by libc in a way similar to clone() (with the child executing
> a separate handler function), or if it was used in the implementation
> of some higher-level process-spawning API, it would be a perfectly
> fine API?
No, there is still a problem with SIGTSTP handling because we cannot
atomically unmask the signal during execve. We need to unblock SIGTSTP
before execve in the new process, but this means that it can get
suspended by SIGTSTP. Consequently, the execve never happens and the
original process is stuck in vfork:
posix_spawn: parent can get stuck in uninterruptible sleep if child
receives SIGTSTP early enough
<https://inbox.sourceware.org/libc-help/2921668c-773e-465d-9480-0abb6f979bf9@www.fastmail.com/>
More on the low-level side, it's difficult to make sure that execve gets
a consistent snapshot of the environ vector. Both vfork and execve need
to be async-signal-safe. Any locking or memory allocation (except for
the stack …) persists in the original process after vfork returns. The
environ vector can be large, so making a copy on the stack is not ideal.
It's even harder for getenv/setenv/unsetenv implementations that use
locking instead of software transactional memory.
In general, I prefer the vfork+execve API over things like posix_spawn
because eventually, you have dependencies between the syslets, or need
control flow. This introduces a lot of complexity. Conceptually,
vfork+execve is much simpler, and in many ways quite safe (even mutexes
work as long as they do not need a correct TID).
Thanks,
Florian
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Andy Lutomirski @ 2026-06-09 0:01 UTC (permalink / raw)
To: Christian Brauner
Cc: Li Chen, Kees Cook, Alexander Viro, linux-fsdevel, linux-api,
linux-kernel, linux-mm, linux-arch, linux-doc, linux-kselftest,
x86, Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <20260528-madig-fachrichtung-fehlinformation-61117ba640da@brauner>
On Thu, May 28, 2026 at 4:05 AM Christian Brauner <brauner@kernel.org> wrote:
>
> On Thu, May 28, 2026 at 05:52:21PM +0800, Li Chen wrote:
> > Hi,
> >
> > This is an early RFC for an idea that is probably still rough in both the
> > UAPI and implementation details. Sorry for the rough edges; I am sending
> > it now to check whether this direction is worth pursuing and to get
> > feedback on the kernel/userspace boundary.
>
> The idea of having a builder api for exec isn't all that crazy. But it
> should simply be built on top of pidfds and thus pidfs itself instead.
> It has all the basic infrastructure in place already. Any implementation
> should also allow userspace to implement posix_spawn() on top of it.
>
> fd = pidfd_open(0, PIDFD_EMPTY /* or better name */)
>
> pidfd_config(fd, ...) // modeled similar to fsconfig()
>
After contemplating this for a bit... why pidfd? Doesn't a pidfd
refer to an actual process that is, or at least was, running? This
new thing is a process that we are contemplating spawning. I can
imagine that basically all pidfd APIs would be a bit confused by the
nonexistence of the process in question.
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: John Ericson @ 2026-06-08 23:06 UTC (permalink / raw)
To: Li Chen, Christian Brauner
Cc: Kees Cook, Al Viro, linux-fsdevel, linux-api, LKML, linux-mm,
linux-arch, linux-doc, linux-kselftest, x86, Arnd Bergmann,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, H. Peter Anvin, Jan Kara, Jonathan Corbet,
Shuah Khan
In-Reply-To: <19e8113d290.893abab26142069.5024234139508454104@linux.beauty>
Hi all,
I am happy to see this thread appear. I emailed Christian and others ~5 years
ago about this in this thread[1]; it would be great to see it finally happen!
I very much agree that the new process spawning should be pidfd based. I also
want to emphasize that the crux of the matter is that code needed to set up the
initial unscheduled process --- which I do think should be "real state" and
more than a mere template --- is currently chopped up between clone and exec.
So the real meat of the implementation would be factoring out a bunch of stuff
so it can be reused in both the legacy clone+exec and modern code paths.
I'll say a bit more about this "real state" vs "mere template" distinction,
which is that the latter is effectively some sort of ad-hoc operation batching
language, and always runs the risk of falling behind what the kernel actually
supports. The "real state" approach, where we have honest-to-goodness process
state, just in some partially initialized fashion and thus it's not yet
scheduled, always supports everything the kernel supports in principle.
Yes, alternative syscalls that specify which "embryonic" process (as opposed to
always the current active process) need to be created, but that is less bad
than trying to stuff things into flags etc. for a single existing system call,
and also one can imagine a world (as described in
https://catern.com/rsys21.pdf) where the exact "which process?" parameter
starts getting added to new process modifying machinery by *default*, with a
sentinel value analogous to `AT_FDCWD` used to mean "the current process" for
the legacy used-between-fork-and-exec usecase.
---
Anyways, years ago, after taking a glance at the relevant code in Linux and
FreeBSD, I figured that it would be easier for me personally to first implement
this functionality in FreeBSD, and then, once I had a feel for some of the
refactoring, take a stab at it in Linux. This is because Linux's feature set,
especially things like `binfmt_misc`, makes its clone and exec quite a bit more
complex, and thus the (IMO) necessary heavy refactoring quite a bit more
extensive too.
I never got around to it in the 5 years, but these days, with LLMs, doing an
"exploratory refactor" (to get a sketch of a patch that is fodder for discussion
not yet fit for actual submission) is much easier. So inspired by this thread, I
took a few hours to do the exploratory FreeBSD refactor in [2]. The man page for
the new syscalls, [3], might be a good place to start reading. (This, being from
a FreeBSD patch, describes the change in terms of "proc fds", but the switch to
Linux's "pidfds" should be self-explanatory. The former after all inspired the
latter.)
Hope discussion of such a patch isn't too off topic here, but there is an
interesting thing to note that would also apply to a Linux implementation. It
took *more* factored out helper functions than I thought. The current count is
over 15(!) --- there didn't seem to be a way to build both the old and new way
of doing things with fewer, coarser building blocks. Now, granted, maybe
someone more familiar with either kernel than me could do a better job, but I
think it will still be a number of functions. This indicates just how much
untangling there is to do. And the number will surely be much higher for Linux.
[1]: https://lore.kernel.org/all/f8457e20-c3cc-6e56-96a4-3090d7da0cb6@JohnEricson.me/
[2]: https://github.com/obsidiansystems/freebsd-src/commit/better-proc-spawn
239dcdefe6ad244e58d998155b527375e5293ff7 for posterity
[3]: https://raw.githubusercontent.com/obsidiansystems/freebsd-src/refs/heads/better-proc-spawn/lib/libsys/proc_new.2
On Sun, May 31, 2026, at 10:47 PM, Li Chen wrote:
> Hi Christian,
>
> Thanks a lot for your great review!
>
> ---- On Thu, 28 May 2026 19:02:53 +0800 Christian Brauner <brauner@kernel.org> wrote ---
> > On Thu, May 28, 2026 at 05:52:21PM +0800, Li Chen wrote:
> > > Hi,
> > >
> > > This is an early RFC for an idea that is probably still rough in both the
> > > UAPI and implementation details. Sorry for the rough edges; I am sending
> > > it now to check whether this direction is worth pursuing and to get
> > > feedback on the kernel/userspace boundary.
> >
> > The idea of having a builder api for exec isn't all that crazy. But it
> > should simply be built on top of pidfds and thus pidfs itself instead.
> > It has all the basic infrastructure in place already.
>
> Yes, that makes a lot more sense. I was staring too hard at the "hot
> executable" part and made the cache/template the API, which was probably
> the wrong thing to expose. Sorry about that.
>
> > Any implementation
> > should also allow userspace to implement posix_spawn() on top of it.
>
> That's so cool, and this is a really useful point. I had not thought about this as
> something that could sit under posix_spawn(), but that makes the target
> much clearer. It should be a generic exec/spawn builder first, and the
> agent use case should just be one user of it.
>
> > fd = pidfd_open(0, PIDFD_EMPTY /* or better name */)
> >
> > pidfd_config(fd, ...) // modeled similar to fsconfig()
>
> Reusing pidfd_open() with an empty target is nice because it keeps the API close
> to pidfds, but I wonder if a separate entry point such as
> pidfd_spawn_open() or pidfd_create() would make the "new process
> builder" case a bit more explicit? Either way, the configuration side
> being fsconfig-like makes sense to me.
Yeah check out my syscalls [3] on that front. It's important to design the
workflow / state machine in a good way. Performance/efficiency, security (share
less state/privileges by default!), and extensibility (where will newer
concepts, like a new type of namespace, fit in?) are all competing concerns,
but I think they mostly pull in the same direction. (Only no ambient authority,
back compat, and extensibility exist in some tension.)
> Thanks again for pointing me in this direction. It helps a lot.
>
> Regards,
> Li
Glad you are sold on pidfds, and more broadly, best of luck! You'll be a hero
to everyone else that has wanted this over the years :)
John
^ permalink raw reply
* Re: [PATCH 2/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: David Hildenbrand (Arm) @ 2026-06-08 18:42 UTC (permalink / raw)
To: Alexander Gordeev, Askar Safin
Cc: linux-fsdevel, Christian Brauner, Alexander Viro, Jan Kara,
linux-kernel, linux-mm, linux-api, netdev, Linus Torvalds,
Matthew Wilcox, Jens Axboe, Christoph Hellwig, David Howells,
Andrew Morton, Pedro Falcato, Miklos Szeredi, patches, linux-s390,
linux-next
In-Reply-To: <20260608171917.3195488Afc-agordeev@linux.ibm.com>
On 6/8/26 19:19, Alexander Gordeev wrote:
> On Sun, May 31, 2026 at 01:01:06AM +0000, Askar Safin wrote:
>> vmsplice behavior on writable pipe became equivalent to pwritev2.
>> vmsplice behavior on readable pipe already was nearly
>> equivalent to preadv2, but I made this explicit. I. e. I made it
>> obvious from code that vmsplice now is equivalent to preadv2/pwritev2.
>>
>> Also I moved vmsplice to fs/read_write.c, because now it arguably
>> belongs there.
>>
>> Note that SPLICE_F_NONBLOCK behavior slightly changed: previously
>> vmsplice ignored whether the pipe was opened with O_NONBLOCK, and mode
>> of operation depended on whether SPLICE_F_NONBLOCK was passed only.
>> Now the operation will be non-blocking if O_NONBLOCK was passed when
>> opening *or* SPLICE_F_NONBLOCK was passed to vmsplice. Previous
>> behavior was arguably buggy, and new behavior is arguably better.
>>
>> Now SPLICE_F_GIFT is always ignored by all 3 syscalls: splice, tee
>> and vmsplice.
>>
>> Signed-off-by: Askar Safin <safinaskar@gmail.com>
>> ---
>> fs/read_write.c | 23 +++++
>> fs/splice.c | 192 +--------------------------------------
>> include/linux/skbuff.h | 4 +-
>> include/linux/splice.h | 2 +-
>> include/linux/syscalls.h | 4 +-
>> 5 files changed, 29 insertions(+), 196 deletions(-)
>
> Hi All,
>
> This patch as commit e2c0b2368081b ("vmsplice: make vmsplice a trivial
> wrapper for preadv2/pwritev2") in linux-next on s390 causes the selftest
> tools/testing/selftests/mm/cow.c to hang:
>
> # [RUN] vmsplice() + unmap in child ... with PTE-mapped THP (128 kB)
>
> Recently there has been changes in THP area, so the problem is not
> necessary linked to this patch per se.
If we reach 128 kB, then 64 kB likely worked. Which might hint at a similar problem
as found by the vmsplice01 ltp test case (blocking instead of returning once the
pipe is full).
https://lore.kernel.org/r/20260603-raumfahrt-unmerklich-ertrugen-c4ecae70d5f9@brauner
--
Cheers,
David
^ permalink raw reply
* Re: [PATCH 2/3] vmsplice: make vmsplice a trivial wrapper for preadv2/pwritev2
From: Alexander Gordeev @ 2026-06-08 17:19 UTC (permalink / raw)
To: Askar Safin
Cc: linux-fsdevel, Christian Brauner, Alexander Viro, Jan Kara,
linux-kernel, linux-mm, linux-api, netdev, Linus Torvalds,
Matthew Wilcox, Jens Axboe, Christoph Hellwig, David Howells,
Andrew Morton, David Hildenbrand, Pedro Falcato, Miklos Szeredi,
patches, linux-s390, linux-next
In-Reply-To: <20260531010107.1953702-3-safinaskar@gmail.com>
On Sun, May 31, 2026 at 01:01:06AM +0000, Askar Safin wrote:
> vmsplice behavior on writable pipe became equivalent to pwritev2.
> vmsplice behavior on readable pipe already was nearly
> equivalent to preadv2, but I made this explicit. I. e. I made it
> obvious from code that vmsplice now is equivalent to preadv2/pwritev2.
>
> Also I moved vmsplice to fs/read_write.c, because now it arguably
> belongs there.
>
> Note that SPLICE_F_NONBLOCK behavior slightly changed: previously
> vmsplice ignored whether the pipe was opened with O_NONBLOCK, and mode
> of operation depended on whether SPLICE_F_NONBLOCK was passed only.
> Now the operation will be non-blocking if O_NONBLOCK was passed when
> opening *or* SPLICE_F_NONBLOCK was passed to vmsplice. Previous
> behavior was arguably buggy, and new behavior is arguably better.
>
> Now SPLICE_F_GIFT is always ignored by all 3 syscalls: splice, tee
> and vmsplice.
>
> Signed-off-by: Askar Safin <safinaskar@gmail.com>
> ---
> fs/read_write.c | 23 +++++
> fs/splice.c | 192 +--------------------------------------
> include/linux/skbuff.h | 4 +-
> include/linux/splice.h | 2 +-
> include/linux/syscalls.h | 4 +-
> 5 files changed, 29 insertions(+), 196 deletions(-)
Hi All,
This patch as commit e2c0b2368081b ("vmsplice: make vmsplice a trivial
wrapper for preadv2/pwritev2") in linux-next on s390 causes the selftest
tools/testing/selftests/mm/cow.c to hang:
# [RUN] vmsplice() + unmap in child ... with PTE-mapped THP (128 kB)
Recently there has been changes in THP area, so the problem is not
necessary linked to this patch per se.
Please, let me know if you need any additional information.
Thanks!
^ permalink raw reply
* Re: [LTP] [PATCH 0/5] vmsplice: fix some problems in my previous vmsplice patchset
From: Askar Safin @ 2026-06-08 17:12 UTC (permalink / raw)
To: andrea.cervesato
Cc: akpm, axboe, brauner, collin.funk1, david.laight.linux, david,
dhowells, fuse-devel, hch, jack, joannelkoong, kernel, linux-api,
linux-fsdevel, linux-kernel, linux-mm, ltp, luto, metze, miklos,
netdev, patches, pfalcato, rostedt, safinaskar, torvalds, viro, w,
willy
In-Reply-To: <6a26aa28.283787d8.1f1282.ba36@mx.google.com>
"Andrea Cervesato" <andrea.cervesato@suse.com>:
> Hi Askar,
>
> the patch-set doesn't apply:
This is patchset for Linux kernel.
(It is expected it will fix some failing LTP tests, among other things.)
--
Askar Safin
^ permalink raw reply
* Re: [RFC PATCH v1 00/13] exec: add spawn templates for repeated executable startup
From: Jann Horn @ 2026-06-08 15:02 UTC (permalink / raw)
To: Mateusz Guzik, Christian Brauner
Cc: Li Chen, Kees Cook, Alexander Viro, linux-fsdevel, linux-api,
linux-kernel, linux-mm, linux-arch, linux-doc, linux-kselftest,
x86, Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, H. Peter Anvin, Jan Kara,
Jonathan Corbet, Shuah Khan
In-Reply-To: <vealb52tv5suireenkke4lul2l3wbnaul2rp3ea545ly5wa5ty@yk3aksvp7skt>
On Thu, May 28, 2026 at 2:55 PM Mateusz Guzik <mjguzik@gmail.com> wrote:
> This problem is dear to my heart and I have been pondering it on and off
> for some time now. The entire fork + exec idiom is terrible and needs to
> be retired.
It seems to me like vfork+exec is a decent UAPI building block, on
which you can build nice-looking userspace APIs, though I agree that
this is not an ideal direct interface for application code.
> Additionally there is a known problem where transiently copied file
> descriptors on fork + exec cause a headache in multithreaded programs
> doing something like this in parallel. I only did cursory reading, it
> seems your patchset keeps the same problem in place.
I think we almost have UAPI that would let you avoid this issue?
You can use clone() with CLONE_FILES, then unshare the FD table with
close_range(3, UINT_MAX, CLOSE_RANGE_UNSHARE). That is not currently
implemented to be atomic with stuff that happens on other threads, but
if we changed that, and it doesn't provide a good way to carry some
FDs across, but it feels to me like this could be fixed with a variant
of close_range() that removes O_CLOEXEC FDs except ones listed in an
array.
> There are numerous impactful ways to speed up execs both in terms of
> single-threaded cost and their multicore scalability, most of which
> would be immediately usable by all programs without an opt-in. imo these
> needs to be exhausted before something like a "template" can be
> considered.
(I think probably a large part of this would be stuff that happens in
userspace, like dynamic linking.)
> Per the above, the primary win would stem from *NOT* messing with mm.
As you write below, I think we have that with CLONE_MM? The C function
vfork() is kind of a terrible API because of its returns-twice
behavior, but I think if process cloning with CLONE_VM|CLONE_VFORK was
wrapped by libc in a way similar to clone() (with the child executing
a separate handler function), or if it was used in the implementation
of some higher-level process-spawning API, it would be a perfectly
fine API?
Or am I misunderstanding what you mean by "messing with mm"?
> As in, whatever the interface, it needs to create an "empty" target
> process (for lack of a better term).
>
> In terms of userspace-visible APIs, a clean solution escapes me.
I think we already have relatively good API for this - you can use
clone() to create something that initially shares almost all the state
that a thread would, and then incrementally unshare resources and go
through execve().
^ permalink raw reply
* Re: [LTP] [PATCH 0/5] vmsplice: fix some problems in my previous vmsplice patchset
From: Andrea Cervesato @ 2026-06-08 11:40 UTC (permalink / raw)
To: Askar Safin
Cc: linux-fsdevel, Christian Brauner, Alexander Viro, Jan Kara,
The 8472, patches, David Howells, linux-mm, Collin Funk,
Joanne Koong, Miklos Szeredi, David Laight, Matthew Wilcox,
Christoph Hellwig, Steven Rostedt, fuse-devel, David Hildenbrand,
Pedro Falcato, ltp, Jens Axboe, Stefan Metzmacher, netdev,
linux-kernel, Andy Lutomirski, linux-api, Andrew Morton,
Linus Torvalds, Willy Tarreau
In-Reply-To: <20260606061031.3744880-1-safinaskar@gmail.com>
Hi Askar,
the patch-set doesn't apply:
error: fs/read_write.c: does not exist in index
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Applying: vmsplice: open-code do_writev and do_readv
Patch failed at 0001 vmsplice: open-code do_writev and do_readv
https://github.com/linux-test-project/ltp-agent/actions/runs/27129052434/job/80065058557#step:8:21
Please update it to a new version after rebasing with the upstream master
branch.
Regards,
--
Andrea Cervesato
SUSE QE Automation Engineer Linux
andrea.cervesato@suse.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox