{"record":{"id":"62ff8c974db0d4b4","repo":"microsoft/garnet","slug":"start-nameof-start-len-len-exceeds-length","errorCode":null,"errorMessage":"start {nameof(start)} + len {len} exceeds length {length}","messagePattern":"start (.+?) \\+ len (.+?) exceeds length (.+?)","errorType":"exception","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/OverflowByteArray.cs","lineNumber":58,"sourceCode":"\n        /// <inheritdoc/>\n        public override string ToString() => $\"Len {Length}, IsEmpty {IsEmpty}, sOffset {StartOffset}, eOffset {EndOffset}, HeapMemSize {HeapMemorySize}, TotSize {TotalSize}\";\n\n        /// <summary>ReadOnlySpan of data between offsets</summary>\n        internal readonly ReadOnlySpan<byte> ReadOnlySpan => Array.AsSpan(StartOffset, Length);\n        /// <summary>ReadOnlySpan of data between offsets</summary>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public readonly ReadOnlySpan<byte> AsReadOnlySpan(int start)\n        {\n            var length = Length;\n            return start <= length ? Array.AsSpan(StartOffset + start, length - start) : throw new ArgumentOutOfRangeException(nameof(start));\n        }\n        /// <summary>ReadOnlySpan of data between offsets</summary>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public readonly ReadOnlySpan<byte> AsReadOnlySpan(int start, int len)\n        {\n            var length = Length;\n            return ((ulong)(uint)start + (uint)len <= (uint)length) ? Array.AsSpan(StartOffset + start, len) : throw new ArgumentOutOfRangeException($\"start {nameof(start)} + len {len} exceeds length {length}\");\n        }\n\n        /// <summary>Span of data between offsets</summary>\n        internal readonly Span<byte> Span => Array.AsSpan(StartOffset, Length);\n        /// <summary>Span of data between offsets</summary>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public readonly Span<byte> AsSpan(int start)\n        {\n            var length = Length;\n            return start <= length ? Array.AsSpan(StartOffset + start, length - start) : throw new ArgumentOutOfRangeException(nameof(start));\n        }\n        /// <summary>ReadOnlySpan of data between offsets</summary>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public readonly Span<byte> AsSpan(int start, int len)\n        {\n            var length = Length;\n            return ((ulong)(uint)start + (uint)len <= (uint)length) ? Array.AsSpan(StartOffset + start, len) : throw new ArgumentOutOfRangeException($\"start {nameof(start)} + len {len} exceeds length {length}\");\n        }","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/OverflowByteArray.cs#L40-L76","documentation":"AsReadOnlySpan(int start, int len) returns len bytes of the valid region beginning at start. It uses an overflow-safe check — (uint)start + (uint)len <= (uint)length — and throws ArgumentOutOfRangeException when the requested window runs past Length. This is the two-argument bounds guard for native-pointer slice creation.","triggerScenarios":"Calling AsReadOnlySpan(start, len) where start+len exceeds Length, including the integer-overflow case where a negative start or len would otherwise wrap past the end. Triggered by reading more value bytes than the record stores, or miscomputing len from header fields.","commonSituations":"Reading a value field of declared length N from an overflow region shorter than N (corrupt/truncated record); copying a fixed-size header that the shrunken record no longer fully contains; arithmetic that adds StartOffset twice.","solutions":["Clamp/validate that (ulong)(uint)start + (ulong)(uint)len <= (ulong)(uint)oba.Length before calling.","Verify the source record's ActualSize/filler length matches the len you intend to read.","Use the record's RecordFieldInfo to obtain authoritative field lengths instead of recomputing.","Treat any negative start/len as a programmer error and reject before the call."],"exampleFix":"// before\nvar span = overflow.AsReadOnlySpan(offset, len);\n\n// after\nif ((ulong)(uint)offset + (ulong)(uint)len > (ulong)(uint)overflow.Length)\n    throw new InvalidOperationException($\"window {offset}+{len} > {overflow.Length}\");\nvar span = overflow.AsReadOnlySpan(offset, len);","handlingStrategy":"validation","validationCode":"if ((ulong)(uint)start + (ulong)(uint)len > (ulong)(uint)overflow.Length)\n    throw new InvalidOperationException($\"window {start}+{len} > Length {overflow.Length}\");\nvar span = overflow.AsReadOnlySpan(start, len);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the overflow-safe (uint)+(uint) check rather than int arithmetic to catch negative inputs.","Cross-check len against the record's declared field length before slicing.","Reject negative start/len at the call site."],"tags":["tsavorite","span","bounds","overflow-array","argumentoutofrange"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}