{"id":"CVE-2026-79979","kind":"vulnerability","aliases":["CVE-2026-79979"],"related":[],"title":"Zephyr arm64: stale TLB entries let user threads read and write evicted memory","description":"The arm64 MMU backend invalidates a single virtual address from the TLB with invalidate_tlb_page() in arch/arm64/core/mmu.c, which issued TLBI VAE1{IS} with an operand of virt >> 12. That operand encodes the ASID in bits [63:48], so the invalidation only ever matched ASID 0 (the kernel page tables) and global entries. Memory domains, however, are allocated ASIDs starting at 1 in arch_mem_domain_init(), and every domain-private leaf created by private_map() is mapped with MT_NG, i.e. non-global. A domain's cached translation therefore survived the invalidation.\n\nAll callers of invalidate_tlb_page() are demand-paging paths. When the eviction algorithm reclaims a frame, page_frame_prepare_locked() in kernel/mmu.c calls arch_mem_page_out(), which marks the kernel leaf invalid, propagates the invalid descriptor into every domain via sync_domains(), and then flushes only ASID 0. do_page_fault() immediately reuses the very same page frame for the incoming mapping through arch_mem_page_in(). A user thread that had already touched the evicted page — a k_mem_partition added with arch_mem_domain_partition_add(), or its own stack mapped by map_thread_stack() — retains a valid EL0 read/write translation to that physical frame under its own ASID.\n\nUnprivileged code running in a user thread can drive memory pressure to force eviction and then keep accessing the stale address, obtaining read and write access to whatever the kernel subsequently placed in that frame: supervisor data, another memory domain's partition, or another thread's stack. This defeats the isolation that CONFIG_USERSPACE is meant to enforce, allowing information disclosure and memory corruption up to full compromise of the system.\n\nThe fix switches the implementation to TLBI VAAE1{IS}, which matches every ASID and both global and non-global entries, so no domain-tagged translation survives a page out, dirty-bit update or access-flag clear. Exploitation requires an arm64 build with both CONFIG_USERSPACE and CONFIG_DEMAND_PAGING enabled, an active backing store, and at least one demand-paged page that is also privately mapped into a user memory domain. The same commit additionally stops globalize_table() from stripping EL0 permission and non-global attributes off domain-private leaves during paging synchronization, which previously caused user threads to lose access to their own partitions and stacks.","summary":"On arm64 builds with user mode and demand paging, the MMU invalidates TLB entries using the wrong ASID, so a user thread keeps access to a physical frame after the kernel evicts and reuses it. A user-mode thread can then read or write supervisor data, another memory domain's partition, or another thread's stack.","severity":"high","cvss":7.8,"epss":0.00144,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["arm64","embedded","rtos","runtime","zephyr"],"not_affected_if":["default-config","no-demand-paging","non-arm64"],"how_to_check":"- Check whether the build has both `CONFIG_USERSPACE` and `CONFIG_DEMAND_PAGING` enabled in the kernel configuration (`.config` or `prj.conf`) for an arm64 target\n- Check whether a backing store is active and whether any demand-paged page is privately mapped into a user memory domain\n- Check the Zephyr version in use against the fixed release (unknown)","fix":"- Update Zephyr to a release that includes the commit switching `invalidate_tlb_page()` to `TLBI VAAE1{IS}` (fixed version: unknown in sources)\n- Until updated, disable `CONFIG_DEMAND_PAGING` on arm64 builds that use user mode (workaround implied by the exploitation conditions)","status":"open","published_at":"2026-10-23T20:17:27+00:00","first_seen_at":"2026-10-23T21:57:34+00:00","triaged_at":"2026-10-25T03:08:03+00:00","updated_at":"2026-10-25T12:38:49+00:00","category":"runtime","confidence":0.7,"triage":{"at":"2026-10-25T03:08:03Z","model":"claude-haiku-5-5","output":{"fix":"- Update Zephyr to a release that includes the commit switching `invalidate_tlb_page()` to `TLBI VAAE1{IS}` (fixed version: unknown in sources)\n- Until updated, disable `CONFIG_DEMAND_PAGING` on arm64 builds that use user mode (workaround implied by the exploitation conditions)","reach":"local","title":"Zephyr arm64: stale TLB entries let user threads read and write evicted memory","impact":"high","summary":"On arm64 builds with user mode and demand paging, the MMU invalidates TLB entries using the wrong ASID, so a user thread keeps access to a physical frame after the kernel evicts and reuses it. A user-mode thread can then read or write supervisor data, another memory domain's partition, or another thread's stack.","category":"runtime","nicknames":[],"who_cares":["zephyr","arm64","embedded","rtos"],"confidence":0.7,"needs_auth":"no","worst_case":"user code reads and writes kernel and other partitions' memory","analyst_note":"Confirm the fixed Zephyr release version and the commit reference; sources only give the NVD entry.","how_to_check":"- Check whether the build has both `CONFIG_USERSPACE` and `CONFIG_DEMAND_PAGING` enabled in the kernel configuration (`.config` or `prj.conf`) for an arm64 target\n- Check whether a backing store is active and whether any demand-paged page is privately mapped into a user memory domain\n- Check the Zephyr version in use against the fixed release (unknown)","needs_analyst":true,"not_affected_if":["default-config","no-demand-paging","non-arm64"],"who_is_affected":"Only arm64 Zephyr builds that enable both user mode (CONFIG_USERSPACE) and demand paging (CONFIG_DEMAND_PAGING) with an active backing store. Builds without demand paging, or on other architectures, are not exposed by this description.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000762,"input_tokens":4052,"output_tokens":714,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2026':15A,18A '-79979':16A,19A '0':127C,227C '1':143C '12':110C '48':119C '63':118C 'access':48B,308C,316C,399C,469C 'access-flag':398C 'activ':417C 'add':261C,267C 'addit':441C 'address':90C,311C 'algorithm':191C 'alloc':139C 'allow':349C 'alreadi':252C 'also':430C 'anoth':72B,78B,328C,334C 'arch':145C,202C,243C,263C 'arch/arm64/core/mmu.c':99C 'arm64':2A,20B,26B,83C,405C 'asid':42B,115C,126C,140C,226C,290C,374C 'attribut':453C 'back':418C 'backend':85C 'bit':117C,395C 'build':27B,406C 'cach':171C 'call':201C 'caller':178C 'caus':464C 'clear':401C 'code':292C 'commit':440C 'compromis':358C 'config':343C,409C,412C 'corrupt':354C 'creat':155C 'cve':14A,17A 'data':71B,327C 'defeat':339C 'demand':32B,185C,413C,425C 'demand-pag':184C,424C 'descriptor':215C 'dirti':394C 'dirty-bit':393C 'disclosur':351C 'domain':74B,136C,147C,152C,169C,218C,221C,265C,330C,386C,437C,456C 'domain-priv':151C,455C 'domain-tag':385C 'drive':299C 'el0':280C,447C 'embed':21B 'enabl':415C 'encod':113C 'enforc':348C 'entri':5A,38B,134C,382C 'ever':124C 'everi':150C,217C,373C 'evict':12A,56B,190C,255C,304C 'exploit':402C 'fault':230C 'fix':363C 'flag':400C 'flush':224C 'forc':303C 'frame':52B,194C,196C,237C,286C,325C 'full':357C 'global':133C,167C,377C,381C,443C,452C 'howev':137C 'i.e':164C 'immedi':231C 'implement':366C 'incom':240C 'inform':350C 'init':148C 'invalid':36B,86C,95C,122C,176C,180C,211C,214C 'isol':341C 'issu':101C 'k':258C 'keep':47B,307C 'kernel':55B,129C,209C,320C 'kernel/mmu.c':200C 'leaf':154C,210C 'least':422C 'leav':458C 'let':6A 'lock':198C 'lose':468C 'map':158C,160C,241C,272C,274C,432C 'mark':207C 'match':125C,372C 'meant':346C 'mem':146C,203C,244C,259C,264C 'memori':13A,73B,135C,300C,329C,353C,436C 'mmu':35B,84C 'mode':30B,63B 'mt':162C 'ng':163C 'non':166C,380C,451C 'non-glob':165C,379C,450C 'obtain':312C 'one':423C 'operand':107C,112C 'page':33B,97C,130C,182C,186C,195C,204C,229C,236C,245C,256C,391C,414C,426C,427C,460C 'partit':76B,260C,266C,332C,473C 'path':187C 'permiss':448C 'physic':51B,285C 'place':322C 'prepar':197C 'pressur':301C 'previous':463C 'privat':153C,157C,431C,457C 'propag':212C 'read':9A,67B,313C 'read/write':281C 'reclaim':192C 'requir':403C 'retain':277C 'reus':58B,232C 'rtos':22B 'run':293C 'runtim':23B 'singl':88C 'stack':81B,271C,276C,337C,475C 'stale':3A,310C 'start':141C 'stop':442C 'store':419C 'strip':446C 'subsequ':321C 'supervisor':70B,326C 'surviv':174C,389C 'switch':364C 'sync':220C 'synchron':461C 'system':361C 'tabl':131C,444C 'tag':387C 'therefor':173C 'thread':8A,46B,64B,79B,249C,275C,297C,335C,466C 'tlb':4A,37B,93C,96C,181C 'tlbi':102C,368C 'touch':253C 'translat':172C,282C,388C 'unprivileg':291C 'updat':396C 'use':39B 'user':7A,29B,45B,62B,248C,296C,435C,465C 'user-mod':61B 'userspac':344C,410C 'vaae1':369C 'vae1':103C 'valid':279C 'via':219C 'virt':109C 'virtual':89C 'whatev':318C 'write':11A,69B,315C 'wrong':41B 'zephyr':1A,24B","nicknames":[],"mitigations":[],"who_affected":"Only arm64 Zephyr builds that enable both user mode (CONFIG_USERSPACE) and demand paging (CONFIG_DEMAND_PAGING) with an active backing store. Builds without demand paging, or on other architectures, are not exposed by this description.","tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T20:17:27+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H","cvss_by":"vulnerabilities@zephyrproject.org","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-79979","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-79979","severity":null,"published_at":"2026-10-23T20:17:27+00:00","modified_at":"2026-10-23T20:17:27+00:00","first_seen_at":"2026-10-23T21:57:34+00:00","fetched_at":"2026-10-23T22:29:04+00:00","note":null},{"source":"epss","external_id":"CVE-2026-79979","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-79979","severity":null,"published_at":null,"modified_at":null,"first_seen_at":"2026-10-25T12:38:49+00:00","fetched_at":"2026-10-25T12:38:49+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}