From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from latitanza.investici.org (latitanza.investici.org [185.218.207.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0D0626A08A for ; Sun, 9 Aug 2026 00:29:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.218.207.228 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786235348; cv=none; b=gLXJ3Kf2re3Typoj93CI/LCpN4GlT/86xNkdWFaIYtLDZ8XUi6U64WhHP8lJedmiDtYzb/wOVX9WV0Jc/Xmb5sMQDBX58UCEmG9pwBd0rSJA+Wnuh/40roNtIrQVccuRY8lQBE1XEAOcRLa75JkFgp1lkFS4XUE4P4RxgVLqvh8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786235348; c=relaxed/simple; bh=18c1AeMg1pZG5oUHRTtkTjkRoqfkZwfnHgw4OxUklio=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=e7yaalL8YIppZVk/Po3pBLhx7uTJ3jaJhudtl43B3Rmq1HFOyNn8migInVoh9av0ETNYaeIDD/WcPQJ3PIwKbwRscd+Jy6NVv4ylXRHo3ZD5XLZvWXEM+XxuLgKCNq+0Yy4aRZOA/h1RIYHLeilzZh/ECMIuJig98vEAr8UaY7s= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=grrlz.net; spf=pass smtp.mailfrom=grrlz.net; dkim=pass (1024-bit key) header.d=grrlz.net header.i=@grrlz.net header.b=ZljwKYNh; arc=none smtp.client-ip=185.218.207.228 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=grrlz.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=grrlz.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=grrlz.net header.i=@grrlz.net header.b="ZljwKYNh" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=grrlz.net; s=stigmate; t=1786235341; bh=QPEsy8jvzXRqb66VFCQSepT3kQmXam9+JUpRIJRT55M=; h=From:To:Cc:Subject:Date:From; b=ZljwKYNhhLYd9vo3p22cgyrXrnB1WrWp2nAYigHBWCCfy4NX0XPREkIkWWZj8BpMw qjr+ynDxVUVSo3ouFY68Ewvhq/sYP4UhRoCHIIS+EtN0T5zyVud8hboalkBz4MY7D/ NArCa3W7uJPJUAzns9RwIIO1AtzNt5t+G0tFsdY4= Received: from mx3.investici.org (unknown [127.0.0.1]) by latitanza.investici.org (Postfix) with UTF8SMTP id 4hHdzd6lRqzGpNM; Sun, 09 Aug 2026 00:29:01 +0000 (UTC) Received: by mx3.investici.org (Postfix) id 4hHdzd4dwgzGpNG; Sun, 09 Aug 2026 00:29:01 +0000 (UTC) From: Bradley Morgan To: akpm@linux-foundation.org Cc: adobriyan@gmail.com, gorcunov@openvz.org, linux-kernel@vger.kernel.org, include@grrlz.net Subject: [PATCH] prctl: fix PR_SET_MM_AUXV losing the forced AT_NULL terminator Date: Sun, 9 Aug 2026 00:29:01 +0000 Message-ID: <20260809002901.32591-1-include@grrlz.net> X-Mailer: git-send-email 2.47.3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit prctl_set_auxv() copies the user vector into a stack buffer, forces AT_NULL on the last two entries there, and then copies only len bytes into mm->saved_auxv. Which is fine until the vector is shorter than the buffer, because then the forced terminator sits past the end of the copy and never lands in saved_auxv at all. The code even says /* Make sure the last entry is always AT_NULL */ and it does, just not in the part that gets copied. So mm->saved_auxv keeps the stale tail from exec. Reproducing it is easy: from a process with CAP_SYS_RESOURCE (just run it as root), call prctl(PR_SET_MM, PR_SET_MM_AUXV, ...) with a vector that has a couple of entries and no AT_NULL inside len (32 bytes on arm64), and then hexdump /proc/self/auxv, or gcore the process and look at the AUXV note with readelf -n. This is arm64, the new vector was just { AT_UID, 0x1111, AT_GID, 0x2222 }: idx before (from exec) after the prctl [0] AT_SYSINFO_EHDR 0x7ed1d6e000 AT_UID 0x1111 <- new [1] AT_MINSIGSTKSZ 0x1270 AT_GID 0x2222 <- new [2] AT_HWCAP 0x119fff AT_HWCAP 0x119fff <- stale [3] AT_PAGESZ 0x1000 AT_PAGESZ 0x1000 <- stale ... 16 more entries ... <- stale [20] AT_NULL 0x0 AT_NULL 0x0 21 entries before the prctl, still 21 after: the two new ones plus all 19 left over from exec. Every consumer walks the vector until AT_NULL, so what they get now is a vector that never existed at exec, the head from the prctl glued onto the tail of the old binary. gdb and crash pull the AUXV note out of coredumps to find AT_PHDR, AT_ENTRY, AT_SYSINFO_EHDR and friends, and a mixed vector points them at the wrong layout. /proc//auxv and PR_GET_AUXV hand the same mess out to live processes too. Nothing crashes, everything just quietly reads a frankenstein auxv. And callers that terminate their own vector hide the whole thing, which is likely why nobody noticed since PR_SET_MM_AUXV landed in 2012. Nothing exciting security wise either, I mean it needs CAP_SYS_RESOURCE to begin with. prctl_set_mm_map() right above already copies the whole buffer for exactly this reason, so just do the same here. user_auxv is zero initialized and only partially filled from userspace, so the rest is zeros and nothing leaks. Fixes: fe8c7f5cbf91 ("c/r: prctl: extend PR_SET_MM to set up more mm_struct entries") Signed-off-by: Bradley Morgan --- kernel/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sys.c b/kernel/sys.c index df69bd71de03..35b538ba843c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2189,7 +2189,7 @@ static int prctl_set_auxv(struct mm_struct *mm, unsigned long addr, BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv)); task_lock(current); - memcpy(mm->saved_auxv, user_auxv, len); + memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv)); task_unlock(current); return 0; -- 2.47.3