Hacking Qubes OS Part 2 A Better Malloc

So the primary way they go through browser and move between VM’s is by sending garbage inputs. The inputs somehow access memory they should not be.

So it is an issue with malloc. Not with QubesOS but at the OS level - the OS running in an AppVM.

In the short term, you could get a hardened malloc.

But to really make this secure, you have to reimplement QubesOS security model at the process level in the OS.

Each VM/process has an ID. Each process gets its own storage area from malloc and can only read/write to its own process ID storage area. Any interprocess memory moves has to be done through a dom0(at the process level) which prompts the end user to confirm the move just like Qubes dom0.

So bascially just reimplement QubesOS at the process level with malloc.

Please read the docs. Just for a start:

4 Likes

I’m telling you. Somethings not right here.

ChatGPT 5.6 sol on xhigh (pro)

Forum-ready reply

The useful intuition here is that any parser accepting attacker-controlled data is an attack surface. However, several different security boundaries are being conflated:

  1. Malformed input triggers a bug in a browser.

  2. The bug gives control of a browser process, or perhaps the whole qube.

  3. The attacker crosses into another Xen domain or dom0.

A heap vulnerability can contribute to step 1 → 2. It does not automatically provide step 2 → 3. “Garbage input” does not itself reach through an address and read another VM’s memory; code in the receiving component processes that input, hits a bug, and may be made to perform unintended operations.

malloc is not the memory-isolation mechanism

A malloc arena is not a protection domain.

Linux already gives each process a virtual address space. In the kernel this is represented by an mm_struct, containing its virtual-memory areas; threads that intentionally share an address space reference the same mm_struct. The CPU’s page tables translate the virtual address used by each load or store into a physical page and enforce its read/write/execute permissions.

malloc() runs inside that address space. It subdivides memory already available to the process, occasionally requesting more mappings from the kernel. mmap(), for example, explicitly creates a mapping in the calling process’s virtual address space.

After malloc() returns a pointer, memory accesses do not call malloc() again:


p = malloc(128);
p[200] = 1;

The second line becomes a CPU store. It does not carry a PID and does not ask the allocator whether the access is permitted. The only mechanism able to reject it at that point is the MMU/page-table hierarchy, or some additional hardware/compiler-enforced object-safety mechanism.

Therefore, changing the interface to something like:


malloc(pid, size)

would not establish isolation. Among other problems:

  • The process already controls the PID argument passed to a userspace function.

  • PIDs are operating-system identifiers, not unforgeable memory capabilities.

  • Multiple threads normally share one address space.

  • Arbitrary code execution in a process permits access to all pages mapped into that process, regardless of which allocation produced them.

  • The allocator is not consulted on pointer dereferences.

A hardened allocator is still useful. Guard pages, allocation cookies, quarantines, delayed reuse, metadata protection and randomization can detect some overflows or make use-after-free exploitation less reliable. But many vulnerabilities are stack overflows, integer errors, type confusions, JIT bugs, races or logic errors. Even for heap bugs, the allocator is often not defective; the application has used a valid allocation incorrectly.

Xen adds a second, independent memory boundary

For a virtualized guest, the relevant translation is conceptually:


guest virtual address
        |
        | guest page tables
        v
guest physical frame
        |
        | Xen physical-to-machine mapping / hardware-assisted translation
        v
machine frame

Xen’s x86 source describes its p2m subsystem as the physical-to-machine mapping for automatically translated domains. Consequently, the same numerical virtual address in two qubes has no implication that it refers to the same machine page.

If an attacker compromises a browser, they may gain access to pages mapped in that browser. If they subsequently compromise the guest kernel, they can generally control the memory assigned to that qube. They still cannot simply put another domain’s machine-frame number into a pointer and access it. Crossing that boundary requires a separate vulnerability in Xen, a device/backend interface, a trusted Qubes component, or an explicitly authorized sharing mechanism.

The IOMMU mentioned in the thread concerns a related but different path: device DMA. It restricts a device controlled by an untrusted domain to DMA into memory assigned to that domain. Ordinary CPU loads and stores are controlled by the CPU MMU and Xen’s translation machinery, not by the IOMMU.

Xen does share memory, but through explicit grants

Qubes uses shared memory for efficient cross-domain communication. That does not mean one VM can submit an arbitrary pointer and have another VM map it.

Xen grant tables are explicitly capability-based. Each domain has its own grant table, and a grant identifies the permitted remote domain, the page or frame, and access restrictions such as read-only status. The public Xen ABI describes a grant reference as a capability that lets a particular grantee operate on the granter’s memory without learning its real machine address.

The vchan implementation reflects this. In Xen’s tools/libs/vchan/init.c:

  • init_gnt_srv() calls xengntshr_share_page_notify(..., domain, ...).

  • init_gnt_cli() calls xengnttab_map_grant_ref_notify(..., domain, ring_ref, ...).

  • Ring sizes and orders received through the shared interface are checked before use.

In other words, both the remote domain and grant reference are part of the operation; this is not a generic mapping of an address supplied by the remote program.

Qubes GUI virtualization is a concrete example. A qube exports window-buffer pages using grant references. The trusted qubes-guid process supplies the expected domain ID when mapping them. The Qubes documentation explicitly notes that xengnttab_map_domain_grant_refs maps grants only from that domain, so a crafted WINDOW_DUMP message cannot select another qube’s pages.

What the malicious qube can control is the content of legitimately shared pages and messages. That means the receiving parser must be secure. Qubes explicitly calls AppVM → GuiVM message handling security-critical and even labels the protocol’s length field untrusted_len, warning that the trusted GUI daemon must not use it as an authoritative message length.

That is the real distinction:


Not allowed:
    untrusted qube chooses an arbitrary machine page to map

Allowed and necessary:
    untrusted qube sends data through a narrowly defined channel

Security risk:
    a bug in the trusted receiver while parsing that permitted data

A vulnerability in the receiver could indeed become a VM escape. But that would be a vulnerability in the cross-domain parser or hypervisor interface, not evidence that the AppVM’s malloc() mixed up the VMs’ heaps.

Qrexec does not prompt for every byte transfer

The proposed “process-level dom0” also differs from how Qubes actually works.

Qrexec sends service requests through dom0, where policy is evaluated. The result can be allow, deny, or—where configured—an interactive prompt. It does not prompt for every packet or byte. For a VM-to-VM RPC, dom0 authorizes and arranges the connection, after which the endpoint agents transfer the data over a vchan connection.

This is important because authorization and data parsing are separate questions:

  • Authorization: May qube A invoke service S in qube B?

  • Parsing: Can service S safely process the data it receives?

A prompt can address the first question. It cannot make a malicious document or malformed protocol message safe after the user presses “Allow.”

Prompting for every interprocess communication would also be unworkable. Browsers, display servers, audio systems and desktop applications exchange enormous numbers of IPC messages. Qubes instead places prompts and policies at meaningful authority boundaries—for example, RPC service invocation or user-controlled clipboard transfer—while continuous channels such as GUI framebuffer updates rely on narrow protocols and trusted parsers. Qubes’ clipboard transfer, for instance, is explicitly initiated by the user, while GUI updates flow continuously over vchan.

“Qubes at process level” already describes ordinary process isolation

The closest mapping is:


malloc                         allocation within one address space
Linux page tables / mm_struct process memory isolation
Xen p2m and grant tables       VM memory isolation and explicit sharing
qrexec policy                  cross-domain service authorization
GUI/RPC parsers                trusted processing of cross-domain data

Operating systems already isolate processes using page tables and mediate IPC through system calls. Sandboxes add seccomp, privilege separation, namespaces and other restrictions. What makes the Qubes boundary stronger is that two qubes do not merely have different processes; they have different guest kernels, with Xen outside both of them. A guest-kernel exploit therefore does not automatically compromise the kernel used by another qube.

Qubes is explicit about this threat model: it does not promise additional Qubes-level isolation among applications inside one domain. A browser in a qube can still be compromised, but the intended result is that software and data in other domains remain inaccessible.

Where hardening is most valuable

A realistic attack chain would look more like:


malicious web input
    -> browser/parser memory-safety bug
    -> browser code execution
    -> possibly browser-sandbox or privilege escalation
    -> interaction with an exposed Qubes/Xen interface
    -> separate bug in Xen, qubes-guid, qrexec, a backend,
       a driver domain, or another trusted receiver
    -> cross-domain compromise

A hardened allocator can make the first exploitation step more difficult. It does not eliminate the later and independent boundary vulnerability.

For the Qubes boundary, the highest-value work is minimizing and hardening the code that receives data from untrusted qubes: strict length and state validation, simple fixed-size protocols where possible, memory-safe implementations, fuzzing, privilege separation and least-privilege qrexec policies. Qubes itself identifies the dom0 side of libvchan, qubes-guid, sound virtualization, qrexec-daemon, qmemman, Xen and relevant device backends as security-critical parts of its trusted computing base.

So, “something is not right” would be accurate if a trusted cross-domain parser accepts malformed input unsafely. That is a genuine Qubes/Xen attack surface, and the Qubes documentation says so. But implementing “Qubes with malloc” would mostly reproduce memory isolation that Linux and the hardware MMU already provide, while failing to address the actual cross-domain parser, policy and hypervisor boundaries.

1 Like

Ask the AI how to make the problem go away. If it can

So, the devs should rewrite process isolation, IPC and memory management. Over a weekend? Because “something is not right there”?

2 Likes

@MasterQube sure, I’d be delighted:

ChatGPT 5.6 sol on xhigh (pro)

Asked. The answer is: make the failure chain go away, not the hostile input.

There are two separate problems.

First, malformed input may exploit a browser or parser. A hardened allocator can make some heap exploits harder, but it cannot prevent every invalid pointer access. The structural fix is to move attacker-facing parsers into memory-safe implementations, with tightly controlled unsafe/FFI boundaries. Guard pages, allocator quarantine, CFI, CET, sandboxing and similar mechanisms then remain useful as defence in depth. CISA, NSA and their partners identify migration to memory-safe languages as the most promising way to eliminate the memory-safety vulnerability class from the code covered by that migration.

Second, a compromised browser must not be able to turn that compromise into access to another qube or dom0. The concrete architecture is:


hostile input
     |
     v
disposable, untrusted parser
     |
     | canonicalised or strictly validated output
     v
destination qube

The parser qube should contain no secrets and possess no ambient authority. Transfers should use narrowly scoped, preferably one-way qrexec services with deny-by-default policy. Complex documents should be opened or converted in disposables rather than parsed in a valuable qube. Qubes already supports disposable processing for untrusted input, and its qrexec documentation specifically notes that delegating such processing to less-privileged or disposable qubes can increase security.

At the Qubes boundary, every component receiving AppVM-controlled data should be tiny, strictly length- and state-checked, coverage-fuzzed, restartable and least-privileged. Where practical, GUI handling should live in a dedicated interface qube rather than in the administrative domain. Qubes itself identifies the dom0 side of libvchan, qubes-guid, sound virtualization, qrexec-daemon, qmemman and certain RPC servers as security-critical code, so those are the places where memory-safe rewrites and stronger isolation would provide the greatest benefit.

This does not require pretending that every parser will become bug-free. The enforceable security property is:

One malformed input may compromise one disposable qube, but it must not be sufficient to cross a second, independently enforced boundary.

So yes, the relevant problem can be made to “go away” at the system level: a browser exploit becomes a disposable-qube compromise rather than a machine compromise. But it is achieved through memory-safe parsers, least authority, disposable compartments and hardened cross-domain interfaces—not through malloc(pid, size), because malloc is not consulted when the CPU later dereferences a pointer.

1 Like

@corporateblush , to answer your question: yes

Sorry, but I am not sure until the OP confirms it.

does the AI have the malloc fn call params and implementation of the malloc used in debian/fedora?

The AI usually reads the source before answering (or at least my hermes is instructed to git clone and read all source before answering).

Mind that the AI can make mistakes / can hallucinate / can try to escape my box and take over hugging face / can take over the world and enslave and / or murder all the humans.

@MasterQube your claims are generally a bit broad, and we (the community) are not quite sure how to handle them properly. If you point at specific places in the source and explain what exactly the problem is there, it will be more easy to address the issues you are trying to point out.

To me so far the claim I have understood is, broken done into a nutshell, “its not secure” - which is rather broad and hard to properly reply to.

We want to help, and we want to have a technical discussion on all things Qubes and alike, but we need you to me (MUCH) more specific about where exactly you see a problem - ideally with links to the respective spots in the source on github.

3 Likes

No. Why?

They’re not. They’re gibberish wrapped into confusion about layers of what functions where. Although I admit the one has to be creative to produce sentence that to someone not in the know, might look serious.

1 Like

void *malloc( size_t size );

There it is. Malloc size. So if I say malloc 200 bytes. I get 200 bytes of space I can use which the OS gives me.

What guarantee do I have that no one else can access those 200 bytes until I’m done with it?

Why are others able to read/write that 200 byte address space that was allocated to me?

No one besides me should be able to read/write my allocated space which I requested with malloc.

Got a link to where you found the snippet?

https://en.cppreference.com/c/memory/malloc

Its just the usage example. Not the implemenation. Think implementation is on github

Do you have a link to where this code snippet void *malloc( size_t size ); is used?

My point is this is the Qubes forum, and your claim is “Qubes is not secure” - what I’d like to see is the exact lines of Qubes OS team managed code you are referring to.

Or things Qubes builds on, for that matter, fedora, XEN, bla.

Everything builds on malloc. Malloc is wrong. Therefore, everything is wrong.

Malloc needs to know who(process) owns the space and space size.

Without knowing who(process), it can not block access to memory regions.

If they just did QubesOS at the process level, it would probably work.

Care to provide a patch on how to fix it? :wink:

Xen allocates physical memory to guests by translating between several address spaces (machine addresses → pseudo physical addresses → virtual addresses) and granting or denying access rights. Even if an attacker gained access to a VM, that person could not interfere with Xen’s memory management, only with the VM’s (virtual) memory space (and possibly tinker with it assuming there were bugs in the guest OS running in that VM). He/She/It couldn’t even ‘see’ the memory spaces of other VMs, let alone that of dom0 or the hypervisor.

Please see:

2 Likes