[tint][ir] Fix UAF in validator

The validator was returning a reference to an error, then adding a note
which can resize the underlying vector in the diagnostic::List.

Add a new method to List to reserve space for N more diagnostics to
prevent the reallocation.

Change-Id: I2de0e25957c26575ba9a94f78030e9f938b7c6fa
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/187661
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 892aa4a..3e0e704 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -447,6 +447,7 @@
 }
 
 diag::Diagnostic& Validator::AddError(const Instruction* inst) {
+    diagnostics_.ReserveAdditional(2);  // Ensure diagnostics don't resize alive after AddNote()
     auto src = Disassembly().InstructionSource(inst);
     auto& diag = AddError(src) << inst->FriendlyName() << ": ";
 
@@ -457,6 +458,7 @@
 }
 
 diag::Diagnostic& Validator::AddError(const Instruction* inst, size_t idx) {
+    diagnostics_.ReserveAdditional(2);  // Ensure diagnostics don't resize alive after AddNote()
     auto src =
         Disassembly().OperandSource(Disassembly::IndexedValue{inst, static_cast<uint32_t>(idx)});
     auto& diag = AddError(src) << inst->FriendlyName() << ": ";
@@ -468,6 +470,7 @@
 }
 
 diag::Diagnostic& Validator::AddResultError(const Instruction* inst, size_t idx) {
+    diagnostics_.ReserveAdditional(2);  // Ensure diagnostics don't resize alive after AddNote()
     auto src =
         Disassembly().ResultSource(Disassembly::IndexedValue{inst, static_cast<uint32_t>(idx)});
     auto& diag = AddError(src) << inst->FriendlyName() << ": ";
diff --git a/src/tint/utils/diagnostic/diagnostic.h b/src/tint/utils/diagnostic/diagnostic.h
index 6471b5a..4b627df 100644
--- a/src/tint/utils/diagnostic/diagnostic.h
+++ b/src/tint/utils/diagnostic/diagnostic.h
@@ -185,6 +185,11 @@
         return Add(std::move(error));
     }
 
+    /// Ensures that the diagnostic list can fit an additional @p count diagnostics without
+    /// resizing. This is useful for ensuring that a reference returned by the AddX() methods is not
+    /// invalidated after another Add().
+    void ReserveAdditional(size_t count) { entries_.Reserve(entries_.Length() + count); }
+
     /// @returns true iff the diagnostic list contains errors diagnostics (or of
     /// higher severity).
     bool ContainsErrors() const { return error_count_ > 0; }