On Tue, Sep 29, 2026 at 08:23:20PM +0300, Andrey Drobyshev wrote: > In its current form, the mechanism for saving CPR FDs into hash table > has flaws. > > The CPR fd hash table uses the same CprFd for both key and value, and > is created with a key destroy function. Inserting a second element > with the same (name, id) key leads to a use after free. According to > GLib docs [1], g_hash_table_insert() keeps the old key, replaces the > value with the new element, and destroys the new key. Thus when > inserting 2 CprFd elements fd1 and fd2 with equal keys we get: > > g_hash_table_insert(table, fd1, fd1); /* table has fd1 -> fd1 */ > g_hash_table_insert(table, fd2, fd2); /* table has fd1 -> fd2, fd2 freed */ > > Any subsequent cpr_find_fd() for that key then returns freed memory. > > This is reachable whenever two CPR-aware objects register fds under the > same name, and it happens long before an actual CPR, since cpr_save_fd() > is typically called from .realize(), i.e. at boot time. > > Apart from the UAF itself, when the user decides to perform CPR migration, > on CPR target we'd get corrupted FD from the hash table and fail somewhere > in .realize() for affected devices. > > As a solution, let's altogether prohibit adding CprFds with duplicate > keys. Since FDs are being registered for a subsequent CPR right at the > startup in .realize(), that'll allow us to error out and exit QEMU (or > fail a device hotplug op) gracefully instead of delaying potential > problems for later. To do so, let's make cpr_save_fd() return 'bool' > instead of 'void' and provide an 'errp' argument to it. That way caller > becomes responsible for checking the return value and propagating the > error. > > Also update all the callers to use the adjusted interface to keep things > compilable and bisectable. > > [1] https://docs.gtk.org/glib/type_func.HashTable.insert.html > > Fixes: a181df93bb3 ("migration/cpr: use hashtable for cpr fds") > Signed-off-by: Andrey Drobyshev <[email protected]>
Reviewed-by: Peter Xu <[email protected]> -- Peter Xu
