copy(m.scratch, v)
                }
                m.mutateBytes(&m.scratch)
-               var s string
-               shdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
-               bhdr := (*reflect.SliceHeader)(unsafe.Pointer(&m.scratch))
-               shdr.Data = bhdr.Data
-               shdr.Len = bhdr.Len
-               vals[i] = s
+               vals[i] = string(m.scratch)
        case []byte:
                if len(v) > maxPerVal {
                        panic(fmt.Sprintf("cannot mutate bytes of length %d", len(v)))
 
 package fuzz
 
 import (
+       "bytes"
        "fmt"
        "os"
        "strconv"
                })
        }
 }
+
+func TestStringImmutability(t *testing.T) {
+       v := []interface{}{"hello"}
+       m := newMutator()
+       m.mutate(v, 1024)
+       original := v[0].(string)
+       originalCopy := make([]byte, len(original))
+       copy(originalCopy, []byte(original))
+       for i := 0; i < 25; i++ {
+               m.mutate(v, 1024)
+       }
+       if !bytes.Equal([]byte(original), originalCopy) {
+               t.Fatalf("string was mutated: got %x, want %x", []byte(original), originalCopy)
+       }
+}
 
 // f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
 //
 // This function should be fast, deterministic, and stateless.
-// None of the pointers to any input data should be retained between executions.
+//
+// No mutatable input arguments, or pointers to them, should be retained between
+// executions of the fuzz function, as the memory backing them may be mutated
+// during a subsequent invocation.
 //
 // This is a terminal function which will terminate the currently running fuzz
 // target by calling runtime.Goexit.