xe_sriov_packet_write_single() keeps a partially written restore packet
in the per-VF pending slot across write() calls.

When the header becomes complete, xe_sriov_packet_init_from_hdr() sets
data->remaining from the user supplied header before initializing the
packet backing storage. If that initialization fails, for example because
the header contains an invalid gt_id, pkt_hdr_write() returns an error
but the pending slot is left pointing at the partially initialized packet.

A later write then reuses that packet, skips the header path because
hdr_remaining is already zero, and reaches pkt_data_write() without
backing storage installed. This can dereference data->vaddr while it is
still NULL.

Drop the pending packet when header initialization fails after the header
has been fully consumed, so the next write starts from a fresh packet.

Fixes: 1ed30397c0b9 ("drm/xe/pf: Add support for encap/decap of bitstream 
to/from packet")
Signed-off-by: Guangshuo Li <[email protected]>
---
 drivers/gpu/drm/xe/xe_sriov_packet.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_sriov_packet.c 
b/drivers/gpu/drm/xe/xe_sriov_packet.c
index 2ae9eff2a7c0..e9c92019862e 100644
--- a/drivers/gpu/drm/xe/xe_sriov_packet.c
+++ b/drivers/gpu/drm/xe/xe_sriov_packet.c
@@ -323,8 +323,9 @@ ssize_t xe_sriov_packet_write_single(struct xe_device *xe, 
unsigned int vfid,
                                     const char __user *buf, size_t len)
 {
        struct xe_sriov_packet **data = pf_pick_pending(xe, vfid);
-       int ret;
        ssize_t copied;
+       bool writing_hdr;
+       int ret;
 
        if (IS_ERR_OR_NULL(*data)) {
                *data = xe_sriov_packet_alloc(xe);
@@ -332,11 +333,21 @@ ssize_t xe_sriov_packet_write_single(struct xe_device 
*xe, unsigned int vfid,
                        return -ENOMEM;
        }
 
-       if ((*data)->hdr_remaining)
+       writing_hdr = (*data)->hdr_remaining;
+       if (writing_hdr)
                copied = pkt_hdr_write(*data, buf, len);
        else
                copied = pkt_data_write(*data, buf, len);
 
+       if (copied < 0) {
+               if (writing_hdr && !(*data)->hdr_remaining) {
+                       kfree(*data);
+                       *data = NULL;
+               }
+
+               rturn copied;
+       }
+
        if ((*data)->hdr_remaining == 0 && (*data)->remaining == 0) {
                ret = xe_sriov_pf_migration_restore_produce(xe, vfid, *data);
                if (ret) {
-- 
2.43.0

Reply via email to