[dawn][native] Spanify CommandIterator::NextData

And also make it return a const span to ensure users don't modify the
command data.

This requires modifying every backend's command iteration to retrieve
Spans instead ot T* from NextData and either unpack it again with
.data() and .size(), or pass the Span further.

Change a couple of sizes in Commands.h to be size_t so that a Span<> is
returned from NextData when that size's type is used.

Bug: 528305452, 532944732, 532946455
Change-Id: If5f3801cf7653ab60c96e7f0767ec61bad697f86
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/323079
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/BindGroupTracker.h b/src/dawn/native/BindGroupTracker.h
index 79b0dd2..12a76d6 100644
--- a/src/dawn/native/BindGroupTracker.h
+++ b/src/dawn/native/BindGroupTracker.h
@@ -49,7 +49,7 @@
     void OnSetBindGroup(BindGroupIndex index,
                         BindGroupBase* bindGroup,
                         uint32_t dynamicOffsetCount,
-                        uint32_t* dynamicOffsets) {
+                        const uint32_t* dynamicOffsets) {
         DAWN_ASSERT(index < kMaxBindGroupsTyped);
         DAWN_ASSERT(dynamicOffsetCount <= kMaxDynamicOffsetsPerBindGroup);
 
@@ -71,7 +71,7 @@
         mBindGroups[index] = bindGroup;
         mDynamicOffsets[index].count = BindingIndex(dynamicOffsetCount);
         auto dynamicOffsetsSpan =
-            DAWN_UNSAFE_TODO(Span<uint32_t>(dynamicOffsets, dynamicOffsetCount));
+            DAWN_UNSAFE_TODO(Span<const uint32_t>(dynamicOffsets, dynamicOffsetCount));
         std::ranges::copy(dynamicOffsetsSpan, mDynamicOffsets[index].offsets.begin());
     }
 
diff --git a/src/dawn/native/CommandAllocator.h b/src/dawn/native/CommandAllocator.h
index aa7b12a..523fa7a 100644
--- a/src/dawn/native/CommandAllocator.h
+++ b/src/dawn/native/CommandAllocator.h
@@ -119,9 +119,10 @@
     }
 
     template <typename T, typename Index>
-        requires(alignof(T) <= kMaxAllocatedCommandAlignment)
-    T* NextData(Index count) {
-        return ReinterpretSpan<T, Index>(NextData(sizeof(T) * count)).data();
+        requires(alignof(T) <= kMaxAllocatedCommandAlignment &&
+                 (std::is_same_v<Index, size_t> || !std::is_integral_v<Index>))
+    ityp::span<Index, const T> NextData(Index count) {
+        return ReinterpretSpan<const T, Index>(NextData(sizeof(T) * count));
     }
 
     // Sets iterator to the beginning of the commands without emptying the list. This method can
diff --git a/src/dawn/native/Commands.cpp b/src/dawn/native/Commands.cpp
index 0f8a231..dd97f95 100644
--- a/src/dawn/native/Commands.cpp
+++ b/src/dawn/native/Commands.cpp
@@ -141,8 +141,8 @@
             case Command::ExecuteBundles: {
                 ExecuteBundlesCmd* cmd = commands->NextCommand<ExecuteBundlesCmd>();
                 auto bundles = commands->NextData<Ref<RenderBundleBase>>(cmd->count);
-                for (size_t i = 0; i < cmd->count; ++i) {
-                    (&DAWN_UNSAFE_TODO(bundles[i]))->~Ref<RenderBundleBase>();
+                for (auto& bundle : bundles) {
+                    (&bundle)->~Ref<RenderBundleBase>();
                 }
                 cmd->~ExecuteBundlesCmd();
                 break;
diff --git a/src/dawn/native/Commands.h b/src/dawn/native/Commands.h
index 4784fd1..5f4eafd 100644
--- a/src/dawn/native/Commands.h
+++ b/src/dawn/native/Commands.h
@@ -411,7 +411,8 @@
 
     BindGroupIndex index = BindGroupIndex(0u);
     Ref<BindGroupBase> group;
-    uint32_t dynamicOffsetCount = 0;
+    // TODO(https://crbug.com/532944732) Use BindingIndex.
+    size_t dynamicOffsetCount = 0;
 };
 
 struct SetImmediatesCmd {
@@ -419,7 +420,7 @@
     ~SetImmediatesCmd();
 
     uint32_t offset = 0;
-    uint32_t size = 0;
+    size_t size = 0;
 };
 
 struct SetIndexBufferCmd {
@@ -455,7 +456,7 @@
 
     Ref<BufferBase> buffer;
     uint64_t offset = 0;
-    uint64_t size = 0;
+    size_t size = 0;
 };
 
 struct WriteTimestampCmd {
diff --git a/src/dawn/native/ImmediatesTracker.h b/src/dawn/native/ImmediatesTracker.h
index 1f3e5f6..459f542 100644
--- a/src/dawn/native/ImmediatesTracker.h
+++ b/src/dawn/native/ImmediatesTracker.h
@@ -78,7 +78,7 @@
     UserImmediatesTrackerBase() {}
 
     // Setters
-    void SetImmediates(uint32_t offset, uint8_t* values, uint32_t size) {
+    void SetImmediates(uint32_t offset, const uint8_t* values, uint32_t size) {
         uint8_t* destData = mContent.template Get<uint8_t>(offsetof(T, userImmediates) + offset);
         if (DAWN_UNSAFE_TODO(memcmp(destData, values, size)) != 0) {
             DAWN_UNSAFE_TODO(memcpy(destData, values, size));
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index cbca593..d85e8c1 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -530,16 +530,16 @@
 
             case Command::WriteBuffer: {
                 WriteBufferCmd* cmd = mCommands.NextCommand<WriteBufferCmd>();
-                uint8_t* data = mCommands.NextData<uint8_t>(cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
 
-                if (cmd->size == 0) {
+                if (data.empty()) {
                     // Skip no-op writes.
                     continue;
                 }
 
                 Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
                 DAWN_TRY(dstBuffer->TrackUsage(commandContext, pendingSerial));
-                DAWN_TRY(dstBuffer->Write(commandContext, cmd->offset, data, cmd->size));
+                DAWN_TRY(dstBuffer->Write(commandContext, cmd->offset, data.data(), data.size()));
 
                 break;
             }
@@ -621,13 +621,14 @@
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
 
-                uint32_t* dynamicOffsets = nullptr;
+                Span<const uint32_t> dynamicOffsets;
                 if (cmd->dynamicOffsetCount > 0) {
                     dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
-                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
-                                                cmd->dynamicOffsetCount, dynamicOffsets);
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), dynamicOffsets.size(),
+                                                dynamicOffsets.data());
 
                 break;
             }
@@ -646,8 +647,9 @@
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = mCommands.NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -880,12 +882,14 @@
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
 
-                uint32_t* dynamicOffsets = nullptr;
+                Span<const uint32_t> dynamicOffsets;
                 if (cmd->dynamicOffsetCount > 0) {
                     dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
-                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
-                                                cmd->dynamicOffsetCount, dynamicOffsets);
+
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), dynamicOffsets.size(),
+                                                dynamicOffsets.data());
 
                 break;
             }
@@ -924,8 +928,9 @@
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = iter->NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = iter->NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -1044,8 +1049,8 @@
             case Command::ExecuteBundles: {
                 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    CommandIterator* iter = DAWN_UNSAFE_TODO(bundles[i])->GetCommands();
+                for (const auto& bundle : bundles) {
+                    CommandIterator* iter = bundle->GetCommands();
                     iter->Reset();
                     while (iter->NextCommandId(&type)) {
                         DAWN_TRY(DoRenderBundleCommand(iter, type));
@@ -1089,7 +1094,7 @@
     switch (command) {
         case Command::InsertDebugMarker: {
             InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
-            std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1));
+            std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1).data());
             commandContext->GetD3DUserDefinedAnnotation()->SetMarker(label.c_str());
             break;
         }
@@ -1102,7 +1107,7 @@
 
         case Command::PushDebugGroup: {
             PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
-            std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1));
+            std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1).data());
             commandContext->GetD3DUserDefinedAnnotation()->BeginEvent(label.c_str());
             break;
         }
diff --git a/src/dawn/native/d3d12/CommandBufferD3D12.cpp b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
index b8ecc17..f40d03f 100644
--- a/src/dawn/native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
@@ -1356,14 +1356,15 @@
 
             case Command::InsertDebugMarker: {
                 InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                const char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1379,14 +1380,15 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                const char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1394,29 +1396,29 @@
             case Command::WriteBuffer: {
                 WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
                 const uint64_t offset = write->offset;
-                const uint64_t size = write->size;
-                uint8_t* data = mCommands.NextData<uint8_t>(size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(write->size);
 
-                if (size == 0) {
+                if (data.empty()) {
                     continue;
                 }
 
                 Buffer* dstBuffer = ToBackend(write->buffer.Get());
 
                 DAWN_TRY(device->GetDynamicUploader()->WithUploadReservation(
-                    size, kCopyBufferToBufferOffsetAlignment,
+                    data.size(), kCopyBufferToBufferOffsetAlignment,
                     [&](UploadReservation reservation) -> MaybeError {
-                        DAWN_UNSAFE_TODO(memcpy(reservation.mappedPointer, data, size));
+                        DAWN_UNSAFE_TODO(
+                            memcpy(reservation.mappedPointer, data.data(), data.size()));
                         [[maybe_unused]] bool cleared;
                         DAWN_TRY_ASSIGN(cleared, dstBuffer->EnsureDataInitializedAsDestination(
-                                                     commandContext, offset, size));
+                                                     commandContext, offset, data.size()));
 
                         dstBuffer->TrackUsageAndTransitionNow(commandContext,
                                                               wgpu::BufferUsage::CopyDst);
                         commandList->CopyBufferRegion(
                             dstBuffer->GetD3D12Resource(), offset,
                             ToBackend(reservation.buffer.Get())->GetD3D12Resource(),
-                            reservation.offsetInBuffer, size);
+                            reservation.offsetInBuffer, data.size());
                         return {};
                     }));
                 break;
@@ -1518,35 +1520,37 @@
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
                 BindGroup* group = ToBackend(cmd->group.Get());
-                uint32_t* dynamicOffsets = nullptr;
-
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
-                bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount,
-                                               dynamicOffsets);
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindingTracker->OnSetBindGroup(cmd->index, group, dynamicOffsets.size(),
+                                               dynamicOffsets.data());
                 break;
             }
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = mCommands.NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
             case Command::InsertDebugMarker: {
                 InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                const char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1562,14 +1566,15 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                const char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1935,14 +1940,15 @@
 
             case Command::InsertDebugMarker: {
                 InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
-                const char* label = iter->NextData<char>(cmd->length + 1);
+                Span<const char> label = iter->NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1958,14 +1964,15 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
-                const char* label = iter->NextData<char>(cmd->length + 1);
+                Span<const char> label = iter->NextData<char>(cmd->length + 1);
 
                 if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
                     // PIX color is 1 byte per channel in ARGB format
+                    // TODO(https://crbug.com/526533386): Prevent format string injection in PIX.
                     constexpr uint64_t kPIXBlackColor = 0xff000000;
                     ToBackend(GetDevice())
                         ->GetFunctions()
-                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
+                        ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label.data());
                 }
                 break;
             }
@@ -1987,22 +1994,23 @@
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
                 BindGroup* group = ToBackend(cmd->group.Get());
-                uint32_t* dynamicOffsets = nullptr;
-
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
-                bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount,
-                                               dynamicOffsets);
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindingTracker->OnSetBindGroup(cmd->index, group, dynamicOffsets.size(),
+                                               dynamicOffsets.data());
                 break;
             }
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = iter->NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = iter->NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -2117,8 +2125,8 @@
                 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
 
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    CommandIterator* iter = DAWN_UNSAFE_TODO(bundles[i])->GetCommands();
+                for (const auto& bundle : bundles) {
+                    CommandIterator* iter = bundle->GetCommands();
                     iter->Reset();
                     while (iter->NextCommandId(&type)) {
                         DAWN_TRY(EncodeRenderBundleCommand(iter, type));
diff --git a/src/dawn/native/metal/CommandBufferMTL.mm b/src/dawn/native/metal/CommandBufferMTL.mm
index 1b79838..f1f5b3a 100644
--- a/src/dawn/native/metal/CommandBufferMTL.mm
+++ b/src/dawn/native/metal/CommandBufferMTL.mm
@@ -1523,9 +1523,9 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                 NSRef<NSString> mtlLabel =
-                    AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
+                    AcquireNSRef([[NSString alloc] initWithUTF8String:label.data()]);
                 [commandContext->GetCommands() pushDebugGroup:mtlLabel.Get()];
                 break;
             }
@@ -1533,10 +1533,9 @@
             case Command::WriteBuffer: {
                 WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
                 const uint64_t offset = write->offset;
-                const uint64_t size = write->size;
-                uint8_t* data = mCommands.NextData<uint8_t>(size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(write->size);
 
-                if (size == 0) {
+                if (data.empty()) {
                     continue;
                 }
 
@@ -1544,10 +1543,12 @@
                 Device* device = ToBackend(GetDevice());
 
                 DAWN_TRY(device->GetDynamicUploader()->WithUploadReservation(
-                    size, kCopyBufferToBufferOffsetAlignment,
+                    data.size(), kCopyBufferToBufferOffsetAlignment,
                     [&](UploadReservation reservation) -> MaybeError {
-                        DAWN_UNSAFE_TODO(memcpy(reservation.mappedPointer, data, size));
-                        dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset, size);
+                        DAWN_UNSAFE_TODO(
+                            memcpy(reservation.mappedPointer, data.data(), data.size()));
+                        dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset,
+                                                                      data.size());
 
                         dstBuffer->TrackUsage();
                         [commandContext->EnsureBlit()
@@ -1555,7 +1556,7 @@
                                  sourceOffset:reservation.offsetInBuffer
                                      toBuffer:dstBuffer->GetMTLBuffer()
                             destinationOffset:offset
-                                         size:size];
+                                         size:data.size()];
                         return {};
                     }));
                 break;
@@ -1693,29 +1694,31 @@
 
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
                 bindGroups.OnSetBindGroup(cmd->index, ToBackend(cmd->group.Get()),
-                                          cmd->dynamicOffsetCount, dynamicOffsets);
+                                          dynamicOffsets.size(), dynamicOffsets.data());
                 break;
             }
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = mCommands.NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
             case Command::InsertDebugMarker: {
                 InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                 NSRef<NSString> mtlLabel =
-                    AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
+                    AcquireNSRef([[NSString alloc] initWithUTF8String:label.data()]);
                 [encoder insertDebugSignpost:mtlLabel.Get()];
                 break;
             }
@@ -1729,9 +1732,9 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                char* label = mCommands.NextData<char>(cmd->length + 1);
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                 NSRef<NSString> mtlLabel =
-                    AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
+                    AcquireNSRef([[NSString alloc] initWithUTF8String:label.data()]);
                 [encoder pushDebugGroup:mtlLabel.Get()];
                 break;
             }
@@ -1948,9 +1951,9 @@
 
             case Command::InsertDebugMarker: {
                 InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
-                char* label = iter->NextData<char>(cmd->length + 1);
+                Span<const char> label = iter->NextData<char>(cmd->length + 1);
                 NSRef<NSString> mtlLabel =
-                    AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
+                    AcquireNSRef([[NSString alloc] initWithUTF8String:label.data()]);
                 [encoder insertDebugSignpost:mtlLabel.Get()];
                 break;
             }
@@ -1964,9 +1967,9 @@
 
             case Command::PushDebugGroup: {
                 PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
-                char* label = iter->NextData<char>(cmd->length + 1);
+                Span<const char> label = iter->NextData<char>(cmd->length + 1);
                 NSRef<NSString> mtlLabel =
-                    AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
+                    AcquireNSRef([[NSString alloc] initWithUTF8String:label.data()]);
                 [encoder pushDebugGroup:mtlLabel.Get()];
                 break;
             }
@@ -2005,21 +2008,23 @@
 
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
                 bindGroups.OnSetBindGroup(cmd->index, ToBackend(cmd->group.Get()),
-                                          cmd->dynamicOffsetCount, dynamicOffsets);
+                                          dynamicOffsets.size(), dynamicOffsets.data());
                 break;
             }
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = iter->NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = iter->NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -2123,8 +2128,8 @@
                 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
 
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    CommandIterator* iter = DAWN_UNSAFE_TODO(bundles[i]->GetCommands());
+                for (const auto& bundle : bundles) {
+                    CommandIterator* iter = bundle->GetCommands();
                     iter->Reset();
                     while (iter->NextCommandId(&type)) {
                         EncodeRenderBundleCommand(iter, type);
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index e2a5891..65f0e79 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -1181,18 +1181,17 @@
             case Command::WriteBuffer: {
                 WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
                 uint64_t offset = write->offset;
-                uint64_t size = write->size;
-                uint8_t* data = mCommands.NextData<uint8_t>(size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(write->size);
 
-                if (size == 0) {
+                if (data.empty()) {
                     continue;
                 }
 
                 Buffer* dstBuffer = ToBackend(write->buffer.Get());
-                DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(offset, size));
+                DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(offset, data.size()));
 
                 DAWN_GL_TRY(gl, BindBuffer(GL_ARRAY_BUFFER, dstBuffer->GetHandle()));
-                DAWN_GL_TRY(gl, BufferSubData(GL_ARRAY_BUFFER, offset, size, data));
+                DAWN_GL_TRY(gl, BufferSubData(GL_ARRAY_BUFFER, offset, data.size(), data.data()));
 
                 dstBuffer->TrackUsage();
                 break;
@@ -1259,12 +1258,14 @@
 
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
-                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
-                                                cmd->dynamicOffsetCount, dynamicOffsets);
+
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), dynamicOffsets.size(),
+                                                dynamicOffsets.data());
                 break;
             }
 
@@ -1283,8 +1284,9 @@
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
-                uint8_t* values = mCommands.NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, values, cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -1549,12 +1551,14 @@
 
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
-                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
-                                                cmd->dynamicOffsetCount, dynamicOffsets);
+
+                // TODO(https://crbug.com/532944732): Spanify BindGroupTracker.
+                bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), dynamicOffsets.size(),
+                                                dynamicOffsets.data());
                 break;
             }
 
@@ -1579,8 +1583,9 @@
 
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
-                uint8_t* values = iter->NextData<uint8_t>(cmd->size);
-                immediates.SetImmediates(cmd->offset, values, cmd->size);
+                Span<const uint8_t> data = iter->NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
             default:
@@ -1679,8 +1684,8 @@
                 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
 
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    CommandIterator* iter = DAWN_UNSAFE_TODO(bundles[i])->GetCommands();
+                for (const auto& bundle : bundles) {
+                    CommandIterator* iter = bundle->GetCommands();
                     iter->Reset();
                     while (iter->NextCommandId(&type)) {
                         DAWN_TRY(DoRenderBundleCommand(iter, type));
diff --git a/src/dawn/native/vulkan/CommandBufferVk.cpp b/src/dawn/native/vulkan/CommandBufferVk.cpp
index 2e7bf37..92e938b 100644
--- a/src/dawn/native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn/native/vulkan/CommandBufferVk.cpp
@@ -1468,11 +1468,11 @@
             case Command::InsertDebugMarker: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+                    Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1498,11 +1498,11 @@
             case Command::PushDebugGroup: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+                    Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1518,28 +1518,27 @@
             case Command::WriteBuffer: {
                 WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
                 const uint64_t offset = write->offset;
-                const uint64_t size = write->size;
-                uint8_t* data = mCommands.NextData<uint8_t>(size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(write->size);
 
-                if (size == 0) {
+                if (data.empty()) {
                     continue;
                 }
 
                 Buffer* dstBuffer = ToBackend(write->buffer.Get());
 
                 DAWN_UNSAFE_TODO(DAWN_TRY(device->GetDynamicUploader()->WithUploadReservation(
-                    size, kCopyBufferToBufferOffsetAlignment,
+                    data.size(), kCopyBufferToBufferOffsetAlignment,
                     [&](UploadReservation reservation) -> MaybeError {
-                        memcpy(reservation.mappedPointer, data, size);
+                        memcpy(reservation.mappedPointer, data.data(), data.size());
 
                         dstBuffer->EnsureDataInitializedAsDestination(recordingContext, offset,
-                                                                      size);
+                                                                      data.size());
                         dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
 
                         VkBufferCopy copy;
                         copy.srcOffset = reservation.offsetInBuffer;
                         copy.dstOffset = offset;
-                        copy.size = size;
+                        copy.size = data.size();
 
                         device->fn.CmdCopyBuffer(commands,
                                                  ToBackend(reservation.buffer)->GetHandle(),
@@ -1624,15 +1623,15 @@
 
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
-
                 BindGroup* bindGroup = ToBackend(cmd->group.Get());
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
-                state.descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount,
-                                                    dynamicOffsets);
+                // TODO(https://crbug.com/532944732): Spanify DescriptorSetTracker.
+                state.descriptorSets.OnSetBindGroup(cmd->index, bindGroup, dynamicOffsets.size(),
+                                                    dynamicOffsets.data());
                 break;
             }
 
@@ -1645,11 +1644,11 @@
             case Command::InsertDebugMarker: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+                    Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1675,11 +1674,11 @@
             case Command::PushDebugGroup: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                    const char* label = mCommands.NextData<char>(cmd->length + 1);
+                    Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1705,9 +1704,9 @@
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = nullptr;
-                value = mCommands.NextData<uint8_t>(cmd->size);
-                state.immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                state.immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -1910,11 +1909,11 @@
             case Command::InsertDebugMarker: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
-                    const char* label = iter->NextData<char>(cmd->length + 1);
+                    Span<const char> label = iter->NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1940,11 +1939,11 @@
             case Command::PushDebugGroup: {
                 if (device->GetGlobalInfo().HasExt(InstanceExt::DebugUtils)) {
                     PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
-                    const char* label = iter->NextData<char>(cmd->length + 1);
+                    Span<const char> label = iter->NextData<char>(cmd->length + 1);
                     VkDebugUtilsLabelEXT utilsLabel;
                     utilsLabel.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
                     utilsLabel.pNext = nullptr;
-                    utilsLabel.pLabelName = label;
+                    utilsLabel.pLabelName = label.data();
                     // Default color to black
                     utilsLabel.color[0] = 0.0;
                     utilsLabel.color[1] = 0.0;
@@ -1960,13 +1959,14 @@
             case Command::SetBindGroup: {
                 SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
                 BindGroup* bindGroup = ToBackend(cmd->group.Get());
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
 
-                state.descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount,
-                                                    dynamicOffsets);
+                // TODO(https://crbug.com/532944732): Spanify DescriptorSetTracker.
+                state.descriptorSets.OnSetBindGroup(cmd->index, bindGroup, dynamicOffsets.size(),
+                                                    dynamicOffsets.data());
                 break;
             }
 
@@ -1998,9 +1998,9 @@
             case Command::SetImmediates: {
                 SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = nullptr;
-                value = iter->NextData<uint8_t>(cmd->size);
-                state.immediates.SetImmediates(cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = iter->NextData<uint8_t>(cmd->size);
+                // TODO(https://crbug.com/532946455): Spanify ImmediateTracker.
+                state.immediates.SetImmediates(cmd->offset, data.data(), data.size());
                 break;
             }
 
@@ -2110,8 +2110,8 @@
                 ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
 
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    CommandIterator* iter = DAWN_UNSAFE_TODO(bundles[i])->GetCommands();
+                for (const auto& bundle : bundles) {
+                    CommandIterator* iter = bundle->GetCommands();
                     iter->Reset();
                     while (iter->NextCommandId(&type)) {
                         DAWN_TRY(EncodeRenderBundleCommand(iter, type));
diff --git a/src/dawn/native/webgpu/CommandBufferHelpers.cpp b/src/dawn/native/webgpu/CommandBufferHelpers.cpp
index 6c657e1..0d799fd 100644
--- a/src/dawn/native/webgpu/CommandBufferHelpers.cpp
+++ b/src/dawn/native/webgpu/CommandBufferHelpers.cpp
@@ -45,16 +45,17 @@
     switch (type) {
         case Command::SetBindGroup: {
             const auto& cmd = *commands.NextCommand<SetBindGroupCmd>();
-            const uint32_t* dynamicOffsetsData =
-                cmd.dynamicOffsetCount > 0 ? commands.NextData<uint32_t>(cmd.dynamicOffsetCount)
-                                           : nullptr;
+            Span<const uint32_t> dynamicOffsets;
+            if (cmd.dynamicOffsetCount != 0) {
+                dynamicOffsets = commands.NextData<uint32_t>(cmd.dynamicOffsetCount);
+            }
+
             schema::CommandBufferCommandSetBindGroupCmd data{{
                 .data = {{
                     .index = uint32_t(cmd.index),
                     .bindGroupId = captureContext.GetId(cmd.group),
-                    .dynamicOffsets = std::vector<uint32_t>(
-                        dynamicOffsetsData,
-                        DAWN_UNSAFE_TODO(dynamicOffsetsData + cmd.dynamicOffsetCount)),
+                    .dynamicOffsets =
+                        std::vector<uint32_t>(dynamicOffsets.begin(), dynamicOffsets.end()),
                 }},
             }};
             Serialize(captureContext, data);
@@ -62,11 +63,11 @@
         }
         case Command::SetImmediates: {
             const auto& cmd = *commands.NextCommand<SetImmediatesCmd>();
-            const uint8_t* values = commands.NextData<uint8_t>(cmd.size);
+            Span<const uint8_t> immediateData = commands.NextData<uint8_t>(cmd.size);
             schema::CommandBufferCommandSetImmediatesCmd data{{
                 .data = {{
                     .offset = cmd.offset,
-                    .data = std::vector<uint8_t>(values, DAWN_UNSAFE_TODO(values + cmd.size)),
+                    .data = std::vector<uint8_t>(immediateData.begin(), immediateData.end()),
                 }},
             }};
             Serialize(captureContext, data);
@@ -83,10 +84,10 @@
     switch (type) {
         case Command::PushDebugGroup: {
             const auto& cmd = *commands.NextCommand<PushDebugGroupCmd>();
-            const char* label = commands.NextData<char>(cmd.length + 1);
+            Span<const char> label = commands.NextData<char>(cmd.length + 1);
             schema::CommandBufferCommandPushDebugGroupCmd data{{
                 .data = {{
-                    .groupLabel = label,
+                    .groupLabel = {label.begin(), label.end()},
                 }},
             }};
             Serialize(captureContext, data);
@@ -99,10 +100,10 @@
         }
         case Command::InsertDebugMarker: {
             const auto& cmd = *commands.NextCommand<InsertDebugMarkerCmd>();
-            const char* label = commands.NextData<char>(cmd.length + 1);
+            Span<const char> label = commands.NextData<char>(cmd.length + 1);
             schema::CommandBufferCommandInsertDebugMarkerCmd data{{
                 .data = {{
-                    .markerLabel = label,
+                    .markerLabel = {label.begin(), label.end()},
                 }},
             }};
             Serialize(captureContext, data);
diff --git a/src/dawn/native/webgpu/CommandBufferWGPU.cpp b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
index 88290d3..c2c9b81 100644
--- a/src/dawn/native/webgpu/CommandBufferWGPU.cpp
+++ b/src/dawn/native/webgpu/CommandBufferWGPU.cpp
@@ -146,19 +146,20 @@
 
             case Command::SetBindGroup: {
                 auto cmd = commands.NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = commands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
+
                 wgpu.computePassEncoderSetBindGroup(passEncoder, static_cast<uint32_t>(cmd->index),
                                                     ToBackend(cmd->group)->GetInnerHandle(),
-                                                    cmd->dynamicOffsetCount, dynamicOffsets);
+                                                    dynamicOffsets.size(), dynamicOffsets.data());
                 break;
             }
             case Command::InsertDebugMarker: {
                 auto cmd = commands.NextCommand<InsertDebugMarkerCmd>();
-                char* label = commands.NextData<char>(cmd->length + 1);
-                wgpu.computePassEncoderInsertDebugMarker(passEncoder, {label, cmd->length});
+                Span<const char> label = commands.NextData<char>(cmd->length + 1);
+                wgpu.computePassEncoderInsertDebugMarker(passEncoder, {label.data(), label.size()});
                 break;
             }
 
@@ -170,8 +171,8 @@
 
             case Command::PushDebugGroup: {
                 auto cmd = commands.NextCommand<PushDebugGroupCmd>();
-                char* label = commands.NextData<char>(cmd->length + 1);
-                wgpu.computePassEncoderPushDebugGroup(passEncoder, {label, cmd->length});
+                Span<const char> label = commands.NextData<char>(cmd->length + 1);
+                wgpu.computePassEncoderPushDebugGroup(passEncoder, {label.data(), label.size()});
                 break;
             }
 
@@ -186,9 +187,9 @@
             case Command::SetImmediates: {
                 auto cmd = commands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = nullptr;
-                value = commands.NextData<uint8_t>(cmd->size);
-                wgpu.computePassEncoderSetImmediates(passEncoder, cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = commands.NextData<uint8_t>(cmd->size);
+                wgpu.computePassEncoderSetImmediates(passEncoder, cmd->offset, data.data(),
+                                                     data.size());
                 break;
             }
 
@@ -293,11 +294,10 @@
                 auto* cmd = commands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = commands.NextData<Ref<RenderBundleBase>>(cmd->count);
                 std::vector<WGPURenderBundle> wgpuBundles;
-                wgpuBundles.reserve(cmd->count);
+                wgpuBundles.reserve(bundles.size());
 
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    wgpuBundles.push_back(
-                        ToBackend(DAWN_UNSAFE_TODO(bundles[i]).Get())->GetInnerHandle());
+                for (const auto& bundle : bundles) {
+                    wgpuBundles.push_back(ToBackend(bundle.Get())->GetInnerHandle());
                 }
                 wgpu.renderPassEncoderExecuteBundles(passEncoder, wgpuBundles.size(),
                                                      wgpuBundles.data());
@@ -369,8 +369,8 @@
 
             case Command::InsertDebugMarker: {
                 auto cmd = commands.NextCommand<InsertDebugMarkerCmd>();
-                char* label = commands.NextData<char>(cmd->length + 1);
-                wgpu.renderPassEncoderInsertDebugMarker(passEncoder, {label, cmd->length});
+                Span<const char> label = commands.NextData<char>(cmd->length + 1);
+                wgpu.renderPassEncoderInsertDebugMarker(passEncoder, {label.data(), label.size()});
                 break;
             }
 
@@ -382,20 +382,20 @@
 
             case Command::PushDebugGroup: {
                 auto cmd = commands.NextCommand<PushDebugGroupCmd>();
-                char* label = commands.NextData<char>(cmd->length + 1);
-                wgpu.renderPassEncoderPushDebugGroup(passEncoder, {label, cmd->length});
+                Span<const char> label = commands.NextData<char>(cmd->length + 1);
+                wgpu.renderPassEncoderPushDebugGroup(passEncoder, {label.data(), label.size()});
                 break;
             }
 
             case Command::SetBindGroup: {
                 auto cmd = commands.NextCommand<SetBindGroupCmd>();
-                uint32_t* dynamicOffsets = nullptr;
-                if (cmd->dynamicOffsetCount > 0) {
+                Span<const uint32_t> dynamicOffsets;
+                if (cmd->dynamicOffsetCount != 0) {
                     dynamicOffsets = commands.NextData<uint32_t>(cmd->dynamicOffsetCount);
                 }
                 wgpu.renderPassEncoderSetBindGroup(passEncoder, static_cast<uint32_t>(cmd->index),
                                                    ToBackend(cmd->group)->GetInnerHandle(),
-                                                   cmd->dynamicOffsetCount, dynamicOffsets);
+                                                   dynamicOffsets.size(), dynamicOffsets.data());
                 break;
             }
 
@@ -425,9 +425,9 @@
             case Command::SetImmediates: {
                 auto cmd = commands.NextCommand<SetImmediatesCmd>();
                 DAWN_ASSERT(cmd->size > 0);
-                uint8_t* value = nullptr;
-                value = commands.NextData<uint8_t>(cmd->size);
-                wgpu.renderPassEncoderSetImmediates(passEncoder, cmd->offset, value, cmd->size);
+                Span<const uint8_t> data = commands.NextData<uint8_t>(cmd->size);
+                wgpu.renderPassEncoderSetImmediates(passEncoder, cmd->offset, data.data(),
+                                                    data.size());
                 break;
             }
 
@@ -498,8 +498,8 @@
             case Command::ExecuteBundles: {
                 auto cmd = commands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = commands.NextData<Ref<RenderBundleBase>>(cmd->count);
-                for (uint32_t i = 0; i < cmd->count; ++i) {
-                    usedResources.renderBundles.push_back(DAWN_UNSAFE_TODO(bundles[i]).Get());
+                for (const auto& bundle : bundles) {
+                    usedResources.renderBundles.push_back(bundle.Get());
                 }
                 break;
             }
@@ -600,8 +600,8 @@
                 const auto& cmd = *commands.NextCommand<ExecuteBundlesCmd>();
                 auto bundles = commands.NextData<Ref<RenderBundleBase>>(cmd.count);
                 std::vector<schema::ObjectId> bundleIds;
-                for (uint32_t i = 0; i < cmd.count; ++i) {
-                    bundleIds.push_back(captureContext.GetId(DAWN_UNSAFE_TODO(bundles[i]).Get()));
+                for (const auto& bundle : bundles) {
+                    bundleIds.push_back(captureContext.GetId(bundle.Get()));
                 }
                 schema::CommandBufferCommandExecuteBundlesCmd data{{
                     .data = {{
@@ -907,12 +907,12 @@
             }
             case Command::WriteBuffer: {
                 const auto& cmd = *commands.NextCommand<WriteBufferCmd>();
-                auto values = mCommands.NextData<uint8_t>(cmd.size);
+                Span<const uint8_t> values = commands.NextData<uint8_t>(cmd.size);
                 schema::CommandBufferCommandWriteBufferCmd data{{
                     .data = {{
                         .bufferId = captureContext.GetId(cmd.buffer.Get()),
                         .bufferOffset = cmd.offset,
-                        .data = std::vector<uint8_t>(values, DAWN_UNSAFE_TODO(values + cmd.size)),
+                        .data = std::vector(values.begin(), values.end()),
                     }},
                 }};
                 Serialize(captureContext, data);
@@ -1111,8 +1111,8 @@
             }
             case Command::InsertDebugMarker: {
                 auto cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
-                char* label = mCommands.NextData<char>(cmd->length + 1);
-                wgpu.commandEncoderInsertDebugMarker(innerEncoder, {label, cmd->length});
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
+                wgpu.commandEncoderInsertDebugMarker(innerEncoder, {label.data(), label.size()});
                 break;
             }
             case Command::PopDebugGroup: {
@@ -1122,16 +1122,16 @@
             }
             case Command::PushDebugGroup: {
                 auto cmd = mCommands.NextCommand<PushDebugGroupCmd>();
-                char* label = mCommands.NextData<char>(cmd->length + 1);
-                wgpu.commandEncoderPushDebugGroup(innerEncoder, {label, cmd->length});
+                Span<const char> label = mCommands.NextData<char>(cmd->length + 1);
+                wgpu.commandEncoderPushDebugGroup(innerEncoder, {label.data(), label.size()});
                 break;
             }
             case Command::WriteBuffer: {
                 auto cmd = mCommands.NextCommand<WriteBufferCmd>();
-                auto data = mCommands.NextData<uint8_t>(cmd->size);
+                Span<const uint8_t> data = mCommands.NextData<uint8_t>(cmd->size);
                 wgpu.commandEncoderWriteBuffer(innerEncoder,
                                                ToBackend(cmd->buffer)->GetInnerHandle(),
-                                               cmd->offset, data, cmd->size);
+                                               cmd->offset, data.data(), data.size());
                 break;
             }
             default:
diff --git a/src/dawn/native/webgpu/RenderBundleWGPU.cpp b/src/dawn/native/webgpu/RenderBundleWGPU.cpp
index bf6f7f3..7ce8ba0 100644
--- a/src/dawn/native/webgpu/RenderBundleWGPU.cpp
+++ b/src/dawn/native/webgpu/RenderBundleWGPU.cpp
@@ -92,8 +92,8 @@
 
         case Command::InsertDebugMarker: {
             auto cmd = commands.NextCommand<InsertDebugMarkerCmd>();
-            char* label = commands.NextData<char>(cmd->length + 1);
-            wgpu.renderBundleEncoderInsertDebugMarker(encoder, {label, cmd->length});
+            Span<const char> label = commands.NextData<char>(cmd->length + 1);
+            wgpu.renderBundleEncoderInsertDebugMarker(encoder, {label.data(), label.size()});
             break;
         }
 
@@ -105,20 +105,20 @@
 
         case Command::PushDebugGroup: {
             auto cmd = commands.NextCommand<PushDebugGroupCmd>();
-            char* label = commands.NextData<char>(cmd->length + 1);
-            wgpu.renderBundleEncoderPushDebugGroup(encoder, {label, cmd->length});
+            Span<const char> label = commands.NextData<char>(cmd->length + 1);
+            wgpu.renderBundleEncoderPushDebugGroup(encoder, {label.data(), label.size()});
             break;
         }
 
         case Command::SetBindGroup: {
             auto cmd = commands.NextCommand<SetBindGroupCmd>();
-            uint32_t* dynamicOffsets = nullptr;
-            if (cmd->dynamicOffsetCount > 0) {
+            Span<const uint32_t> dynamicOffsets;
+            if (cmd->dynamicOffsetCount != 0) {
                 dynamicOffsets = commands.NextData<uint32_t>(cmd->dynamicOffsetCount);
             }
             wgpu.renderBundleEncoderSetBindGroup(encoder, static_cast<uint32_t>(cmd->index),
                                                  ToBackend(cmd->group)->GetInnerHandle(),
-                                                 cmd->dynamicOffsetCount, dynamicOffsets);
+                                                 dynamicOffsets.size(), dynamicOffsets.data());
             break;
         }
 
@@ -148,9 +148,8 @@
         case Command::SetImmediates: {
             auto cmd = commands.NextCommand<SetImmediatesCmd>();
             DAWN_ASSERT(cmd->size > 0);
-            uint8_t* value = nullptr;
-            value = commands.NextData<uint8_t>(cmd->size);
-            wgpu.renderBundleEncoderSetImmediates(encoder, cmd->offset, value, cmd->size);
+            Span<const uint8_t> data = commands.NextData<uint8_t>(cmd->size);
+            wgpu.renderBundleEncoderSetImmediates(encoder, cmd->offset, data.data(), data.size());
             break;
         }
 
diff --git a/src/dawn/tests/unittests/CommandAllocatorTests.cpp b/src/dawn/tests/unittests/CommandAllocatorTests.cpp
index bc93f83..485451e 100644
--- a/src/dawn/tests/unittests/CommandAllocatorTests.cpp
+++ b/src/dawn/tests/unittests/CommandAllocatorTests.cpp
@@ -36,6 +36,7 @@
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "src/dawn/common/Enumerator.h"
 #include "src/dawn/native/CommandAllocator.h"
 
 namespace dawn::native {
@@ -163,9 +164,10 @@
         ASSERT_EQ(immediates->size, mySize);
         ASSERT_EQ(immediates->offset, myOffset);
 
-        uint32_t* values = iterator.NextData<uint32_t>(5u);
-        for (size_t i = 0; i < 5; i++) {
-            ASSERT_EQ(values[i], myValues[i]);
+        Span<const uint32_t> values = iterator.NextData<uint32_t>(size_t{5u});
+        ASSERT_EQ(values.size(), 5u);
+        for (auto [i, value] : Enumerate(values)) {
+            ASSERT_EQ(value, myValues[i]);
         }
 
         hasNext = iterator.NextCommandId(&type);
diff --git a/src/dawn/tests/unittests/native/CommandBufferEncodingTests.cpp b/src/dawn/tests/unittests/native/CommandBufferEncodingTests.cpp
index 84e7e48..37837ed 100644
--- a/src/dawn/tests/unittests/native/CommandBufferEncodingTests.cpp
+++ b/src/dawn/tests/unittests/native/CommandBufferEncodingTests.cpp
@@ -28,6 +28,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/dawn/common/Enumerator.h"
 #include "src/dawn/native/CommandBuffer.h"
 #include "src/dawn/native/Commands.h"
 #include "src/dawn/native/ComputePassEncoder.h"
@@ -174,16 +175,16 @@
                                  std::vector<uint32_t> offsets = {}) {
         return [index, bg, offsets](CommandIterator* commands) {
             auto* cmd = commands->NextCommand<SetBindGroupCmd>();
-            uint32_t* dynamicOffsets = nullptr;
+            Span<const uint32_t> cmdOffsets;
             if (cmd->dynamicOffsetCount > 0) {
-                dynamicOffsets = commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
+                cmdOffsets = commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
             }
 
             ASSERT_EQ(cmd->index, BindGroupIndex(index));
             ASSERT_EQ(ToAPI(cmd->group.Get()), bg.Get());
-            ASSERT_EQ(cmd->dynamicOffsetCount, offsets.size());
-            for (uint32_t i = 0; i < cmd->dynamicOffsetCount; ++i) {
-                DAWN_UNSAFE_TODO(ASSERT_EQ(dynamicOffsets[i], offsets[i]));
+            ASSERT_EQ(cmdOffsets.size(), offsets.size());
+            for (auto [i, cmdOffset] : Enumerate(cmdOffsets)) {
+                ASSERT_EQ(cmdOffset, offsets[i]);
             }
         };
     };