{"id":24703,"date":"2026-09-16T20:32:40","date_gmt":"2026-09-16T12:32:40","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-90015"},"modified":"2026-09-16T20:32:40","modified_gmt":"2026-09-16T12:32:40","slug":"cve-2026-90015","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-90015","title":{"rendered":"CVE-2026-90015 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nxhci: fix lost bounce"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>xhci: fix lost bounce buffers on TDs spanning several ring segments<\/p>\n<p>When a TD reaches a link TRB with data that is not aligned to the<br \/>\nendpoint&#8217;s wMaxPacketSize, xhci_align_td() stages the unalignable tail<br \/>\nthrough the bounce buffer of the ring segment holding that link TRB.<br \/>\nxhci_unmap_td_bounce_buffer() later unmaps it and, for IN transfers,<br \/>\ncopies the data back into the URB&#8217;s buffer.<\/p>\n<p>The enqueue path records the segment that was bounced in td-&gt;bounce_seg,<br \/>\nunder the assumption that a TD never spans more than two ring segments.<br \/>\nThat assumption does not hold: a TD large enough to span three or more<br \/>\nsegments crosses several link TRBs and can be bounced at each of them.<br \/>\nOnly the last one survives in td-&gt;bounce_seg, so every earlier bounce<br \/>\nbuffer is neither copied back nor DMA unmapped.<\/p>\n<p>The URB still completes with actual_length equal to the requested length<br \/>\nand no error, so the transfer looks successful while a wMaxPacketSize<br \/>\nsized hole in the destination buffer silently keeps its previous<br \/>\ncontents. It also leaks a DMA mapping per dropped bounce.<\/p>\n<p>Any sufficiently large and fragmented bulk transfer can hit this. It was<br \/>\nfound with a USB mass storage device behind xHCI backing a dm-verity<br \/>\ntarget with 512 byte hash blocks, where the stale data is detected rather<br \/>\nthan silently consumed. The device enumerates as SuperSpeed, so<br \/>\nwMaxPacketSize is 1024, while dm-bufio issues one 512 byte bio per hash<br \/>\nblock. verity_prefetch_io() makes the block layer merge hundreds of them<br \/>\ninto a single request of up to 512 scatterlist entries of 512 bytes each.<br \/>\nAt 256 TRBs per ring segment such a TD spans three segments, and every<br \/>\nsegment boundary falls on an odd multiple of 512, i.e. unaligned to<br \/>\nwMaxPacketSize. dm-bufio then caches a hash block holding stale data and<br \/>\ndm-verity declares the metadata block corrupted:<\/p>\n<p>  device-mapper: verity: 8:2: metadata block 10850 is corrupted<\/p>\n<p>A reproducer running this under qemu is available at<br \/>\nhttps:\/\/github.com\/baloo\/xhci-verity<\/p>\n<p>The bounce state (bounce_buf, bounce_dma, bounce_len, bounce_offs)<br \/>\nalready lives on the ring segment, so there is nothing extra to track.<br \/>\nKeep recording the last bounced segment in td-&gt;bounce_seg and, on<br \/>\ncompletion, walk the segments from td-&gt;start_seg up to it, unmapping<br \/>\nevery segment that still has a pending bounce.<\/p>\n<p>Stopping at td-&gt;bounce_seg rather than td-&gt;end_seg matters: a bounce<br \/>\nimplies the TD continues past that segment&#8217;s link TRB, so bounce_seg is<br \/>\nalways strictly before end_seg, and a later TD may already have started<br \/>\nin end_seg and been bounced there. Walking that far would copy a foreign<br \/>\nbounce buffer into this URB and unmap it twice. It also keeps the walk<br \/>\ncorrect if a TD ever wraps the whole ring so that end_seg == start_seg.<\/p>\n<p>[mn: Add ring-&gt;num_segs check to prevent unlikely infinite for loop.]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>xhci: fix lost bounce buffers on TDs spanning several ring segments<\/p>\n<p>When a TD reaches a link TRB with data that is not aligned to <\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-24703","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/24703","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=24703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=24703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=24703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}