From nobody Tue Feb 10 23:01:41 2026 Received: from mail-oo1-f74.google.com (mail-oo1-f74.google.com [209.85.161.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20FBB4219FD for ; Mon, 9 Feb 2026 19:06:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770663978; cv=none; b=iZxwuYRyUaTk22O/Ejgp5wKTRTHHj11VUW5EprsoIHlIVPd5DH4rsog2H2X5+QE9lTtxzTSNr+LFbd97+CZHspufjOMAmIjf/Pz4VCn08vQibZOmO9uG3e6pEPFys+6/FNAyF7YJxomCqkaR4pXVpBeSdrkL6DUqZXTipmhK4FI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770663978; c=relaxed/simple; bh=KXOEZJA8uLY54mQOX/8O8xLWaBAubA8yzr+1rqzHlac=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=XCjWMPNzYDNm7DWLA7xjA3bvkr3nMgA6iqEEboyHG1sOl/4jyHBCCn/2qsy3hJ+WpnWwWLR9OWUvRx6fUgMG36qdn75N3k6i8LSbzreaVdfNhy4OHK9SCv8fKJWxFEW1WnooqC1h4cOJXAYGPUStwnJAICutb7AWBlWKCkAOyMc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DFYBuETU; arc=none smtp.client-ip=209.85.161.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--avagin.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DFYBuETU" Received: by mail-oo1-f74.google.com with SMTP id 006d021491bc7-66304fd62ebso20017608eaf.3 for ; Mon, 09 Feb 2026 11:06:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1770663977; x=1771268777; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=IZa/we8GMlNVxSd3WNL/zoRo7U46nDF+PjWI/oy/N/4=; b=DFYBuETUnYfY3e/SMzsgXemtwi1EDx4x83xyKiFq08YCZ6ZAJjHleM39ailVsIoQ2Y n3Kyv8hkMdD8V4b2v1S9MGQthAyloVgF2oe8NWaYg7+OWG41UbCdo8mtbtBzA9pOxByn czaSc2EE1TZEi+fvi5HONTOrjyk0ba60H0tVLB7/W5UTssbW1+/MsmBglEVUDtg/KF4d FCO+jg+BgZBfa2NUfMjkTG2Ip/WAysdxyp2AO3vCX2HX2shwWyP+0fAxmR2SAKqOCvIL avuSCSOu1wZ13v7Q/t3MZ0/1fCupT7EIIAYARc0S4kDE59PNRonZqfmOpfb8o8NOhUa9 g7bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770663977; x=1771268777; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=IZa/we8GMlNVxSd3WNL/zoRo7U46nDF+PjWI/oy/N/4=; b=ucbUiKjDN0a3y8CyMCRlqZK5Af67Gt24BmHsFB1WJSoIxAw5FEeE1dmZxbc1DOPkrg SkF1vwoznxpNo4fSMl6bMAHSQhM2JdcwvSYvKDcyarRLLJ5XQGpWL7Eizoc6ybxxz7DB RsbHU1XshEDa0lR6XrqS9O3fx/9lSP5cGcnsCgaAVtcJS+v2vbUMDQ/abnK1yJxjtq1A ANFVsNZPyOpbQhv6df2gnej1GBszU3Y4Xe/dj4XuE17kuZMRTWriWzmFzgjs1HjkRK94 4CgJ/PjE0KZOn7uI5ac0UWtrpZwwxjdsAJf5igQhj+g9HBBJKCzQ0i4JnNVKtZltZw3x KrQw== X-Forwarded-Encrypted: i=1; AJvYcCUnNi6ZlZ3Yug6X5RHZCxNqb4O+l8P1XBh70DU/JgTf5PDlDOXB1T1Lw7PxTIBmKF2HB8Qa0UG69e1DgrA=@vger.kernel.org X-Gm-Message-State: AOJu0YyLN9peYA1JivFG7fY+j6kjimNxWFRB9oNyvx4o0QTT0p0p1vyz hZEPnuEGuxiQQfR2gb+1tArOISEgaOu6raCW472GQAz3doTK5aeGPgy1LH9G1duyoGua3zcJarf TKbSNAQ== X-Received: from ilbbb5.prod.google.com ([2002:a05:6e02:5:b0:48d:a328:15c4]) (user=avagin job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6820:81d6:b0:65c:fb86:8a8e with SMTP id 006d021491bc7-66d331ce2dbmr5150912eaf.36.1770663977154; Mon, 09 Feb 2026 11:06:17 -0800 (PST) Date: Mon, 9 Feb 2026 19:06:03 +0000 In-Reply-To: <20260209190605.1564597-1-avagin@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260209190605.1564597-1-avagin@google.com> X-Mailer: git-send-email 2.53.0.239.g8d8fc8a987-goog Message-ID: <20260209190605.1564597-3-avagin@google.com> Subject: [PATCH 2/4] exec: inherit HWCAPs from the parent process From: Andrei Vagin To: Kees Cook , Andrew Morton Cc: Cyrill Gorcunov , Mike Rapoport , Alexander Mikhalitsyn , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, criu@lists.linux.dev, Chen Ridong , Christian Brauner , David Hildenbrand , Eric Biederman , Lorenzo Stoakes , Michal Koutny , Andrei Vagin Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduces a mechanism to inherit hardware capabilities (AT_HWCAP, AT_HWCAP2, etc.) from a parent process when they have been modified via prctl. To support C/R operations (snapshots, live migration) in heterogeneous clusters, we must ensure that processes utilize CPU features available on all potential target nodes. To solve this, we need to advertise a common feature set across the cluster. This patch adds a new mm flag MMF_USER_HWCAP, which is set when the auxiliary vector is modified via prctl(PR_SET_MM, PR_SET_MM_AUXV). When execve() is called, if the current process has MMF_USER_HWCAP set, the HWCAP values are extracted from the current auxiliary vector and stored in the linux_binprm structure. These values are then used to populate the auxiliary vector of the new process, effectively inheriting the hardware capabilities. The inherited HWCAPs are masked with the hardware capabilities supported by the current kernel to ensure that we don't report more features than actually supported. This is important to avoid unexpected behavior, especially for processes with additional privileges. Signed-off-by: Andrei Vagin Reviewed-by: Alexander Mikhalitsyn --- fs/binfmt_elf.c | 8 +++--- fs/binfmt_elf_fdpic.c | 8 +++--- fs/exec.c | 61 ++++++++++++++++++++++++++++++++++++++++ include/linux/binfmts.h | 11 ++++++++ include/linux/mm_types.h | 2 ++ kernel/fork.c | 3 ++ kernel/sys.c | 5 +++- 7 files changed, 89 insertions(+), 9 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3eb734c192e9..aec129e33f0b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -246,7 +246,7 @@ create_elf_tables(struct linux_binprm *bprm, const stru= ct elfhdr *exec, */ ARCH_DLINFO; #endif - NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); + NEW_AUX_ENT(AT_HWCAP, bprm->hwcap); NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE); NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC); NEW_AUX_ENT(AT_PHDR, phdr_addr); @@ -264,13 +264,13 @@ create_elf_tables(struct linux_binprm *bprm, const st= ruct elfhdr *exec, NEW_AUX_ENT(AT_SECURE, bprm->secureexec); NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes); #ifdef ELF_HWCAP2 - NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2); + NEW_AUX_ENT(AT_HWCAP2, bprm->hwcap2); #endif #ifdef ELF_HWCAP3 - NEW_AUX_ENT(AT_HWCAP3, ELF_HWCAP3); + NEW_AUX_ENT(AT_HWCAP3, bprm->hwcap3); #endif #ifdef ELF_HWCAP4 - NEW_AUX_ENT(AT_HWCAP4, ELF_HWCAP4); + NEW_AUX_ENT(AT_HWCAP4, bprm->hwcap4); #endif NEW_AUX_ENT(AT_EXECFN, bprm->exec); if (k_platform) { diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index a3d4e6973b29..55b482f03c82 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -629,15 +629,15 @@ static int create_elf_fdpic_tables(struct linux_binpr= m *bprm, */ ARCH_DLINFO; #endif - NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); + NEW_AUX_ENT(AT_HWCAP, bprm->hwcap); #ifdef ELF_HWCAP2 - NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2); + NEW_AUX_ENT(AT_HWCAP2, bprm->hwcap2); #endif #ifdef ELF_HWCAP3 - NEW_AUX_ENT(AT_HWCAP3, ELF_HWCAP3); + NEW_AUX_ENT(AT_HWCAP3, bprm->hwcap3); #endif #ifdef ELF_HWCAP4 - NEW_AUX_ENT(AT_HWCAP4, ELF_HWCAP4); + NEW_AUX_ENT(AT_HWCAP4, bprm->hwcap4); #endif NEW_AUX_ENT(AT_PAGESZ, PAGE_SIZE); NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC); diff --git a/fs/exec.c b/fs/exec.c index 9d5ebc9d15b0..7401efbe4ba0 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1462,6 +1462,17 @@ static struct linux_binprm *alloc_bprm(int fd, struc= t filename *filename, int fl */ bprm->is_check =3D !!(flags & AT_EXECVE_CHECK); =20 + bprm->hwcap =3D ELF_HWCAP; +#ifdef ELF_HWCAP2 + bprm->hwcap2 =3D ELF_HWCAP2; +#endif +#ifdef ELF_HWCAP3 + bprm->hwcap3 =3D ELF_HWCAP3; +#endif +#ifdef ELF_HWCAP4 + bprm->hwcap4 =3D ELF_HWCAP4; +#endif + retval =3D bprm_mm_init(bprm); if (!retval) return bprm; @@ -1780,6 +1791,53 @@ static int bprm_execve(struct linux_binprm *bprm) return retval; } =20 +static void inherit_hwcap(struct linux_binprm *bprm) +{ + int i, n; + +#ifdef ELF_HWCAP4 + n =3D 4; +#elif defined(ELF_HWCAP3) + n =3D 3; +#elif defined(ELF_HWCAP2) + n =3D 2; +#else + n =3D 1; +#endif + + for (i =3D 0; n && i < AT_VECTOR_SIZE; i +=3D 2) { + long val =3D current->mm->saved_auxv[i + 1]; + + switch (current->mm->saved_auxv[i]) { + case AT_NULL: + goto done; + case AT_HWCAP: + bprm->hwcap =3D val & ELF_HWCAP; + break; +#ifdef ELF_HWCAP2 + case AT_HWCAP2: + bprm->hwcap2 =3D val & ELF_HWCAP2; + break; +#endif +#ifdef ELF_HWCAP3 + case AT_HWCAP3: + bprm->hwcap3 =3D val & ELF_HWCAP3; + break; +#endif +#ifdef ELF_HWCAP4 + case AT_HWCAP4: + bprm->hwcap4 =3D val & ELF_HWCAP4; + break; +#endif + default: + continue; + } + n--; + } +done: + mm_flags_set(MMF_USER_HWCAP, bprm->mm); +} + static int do_execveat_common(int fd, struct filename *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, @@ -1856,6 +1914,9 @@ static int do_execveat_common(int fd, struct filename= *filename, current->comm, bprm->filename); } =20 + if (mm_flags_test(MMF_USER_HWCAP, current->mm)) + inherit_hwcap(bprm); + retval =3D bprm_execve(bprm); out_free: free_bprm(bprm); diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 65abd5ab8836..94a3dcf9b1d2 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -2,6 +2,7 @@ #ifndef _LINUX_BINFMTS_H #define _LINUX_BINFMTS_H =20 +#include #include #include #include @@ -67,6 +68,16 @@ struct linux_binprm { unsigned long exec; =20 struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */ + unsigned long hwcap; +#ifdef ELF_HWCAP2 + unsigned long hwcap2; +#endif +#ifdef ELF_HWCAP3 + unsigned long hwcap3; +#endif +#ifdef ELF_HWCAP4 + unsigned long hwcap4; +#endif =20 char buf[BINPRM_BUF_SIZE]; } __randomize_layout; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 78950eb8926d..68c9131dceee 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -1871,6 +1871,8 @@ enum { #define MMF_TOPDOWN 31 /* mm searches top down by default */ #define MMF_TOPDOWN_MASK BIT(MMF_TOPDOWN) =20 +#define MMF_USER_HWCAP 32 /* user-defined HWCAPs */ + #define MMF_INIT_LEGACY_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\ MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK |\ MMF_VM_MERGE_ANY_MASK | MMF_TOPDOWN_MASK) diff --git a/kernel/fork.c b/kernel/fork.c index b1f3915d5f8e..0091315643de 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1103,6 +1103,9 @@ static struct mm_struct *mm_init(struct mm_struct *mm= , struct task_struct *p, =20 __mm_flags_overwrite_word(mm, mmf_init_legacy_flags(flags)); mm->def_flags =3D current->mm->def_flags & VM_INIT_DEF_MASK; + + if (mm_flags_test(MMF_USER_HWCAP, current->mm)) + mm_flags_set(MMF_USER_HWCAP, mm); } else { __mm_flags_overwrite_word(mm, default_dump_filter); mm->def_flags =3D 0; diff --git a/kernel/sys.c b/kernel/sys.c index 8d199cf457ae..6fbd7be21a5f 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2157,8 +2157,10 @@ static int prctl_set_mm_map(int opt, const void __us= er *addr, unsigned long data * not introduce additional locks here making the kernel * more complex. */ - if (prctl_map.auxv_size) + if (prctl_map.auxv_size) { memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv)); + mm_flags_set(MMF_USER_HWCAP, current->mm); + } =20 mmap_read_unlock(mm); return 0; @@ -2190,6 +2192,7 @@ static int prctl_set_auxv(struct mm_struct *mm, unsig= ned long addr, =20 task_lock(current); memcpy(mm->saved_auxv, user_auxv, len); + mm_flags_set(MMF_USER_HWCAP, current->mm); task_unlock(current); =20 return 0; --=20 2.53.0.239.g8d8fc8a987-goog