/// <summary> /// Interprets a pointer within the current segment as capability pointer and returns the according low-level capability object from /// the capability table. Does not mutate this state. /// </summary> /// <param name="offset">Offset relative to this.Offset within current segment</param> /// <returns>the low-level capability object</returns> /// <exception cref="IndexOutOfRangeException">offset negative or out of range</exception> /// <exception cref="InvalidOperationException">capability table not set</exception> /// <exception cref="Rpc.RpcException">not a capability pointer or invalid capability index</exception> internal Rpc.ConsumedCapability DecodeCapPointer(int offset) { if (offset < 0) { throw new IndexOutOfRangeException(nameof(offset)); } if (Caps == null) { throw new InvalidOperationException("Capbility table not set"); } WirePointer pointer = CurrentSegment[Offset + offset]; if (pointer.IsNull) { // Despite this behavior is not officially specified, // the official C++ implementation seems to send null pointers for null caps. return(null); } if (pointer.Kind != PointerKind.Other) { throw new Rpc.RpcException("Expected a capability pointer, but got something different"); } if (pointer.CapabilityIndex >= Caps.Count) { throw new Rpc.RpcException("Capability index out of range"); } return(Caps[(int)pointer.CapabilityIndex]); }
/// <summary> /// Interprets a pointer within the current segment and mutates this state to represent the pointer's target. /// </summary> /// <param name="offset">word offset relative to this.Offset within current segment</param> /// <exception cref="IndexOutOfRangeException">offset negative or out of range</exception> /// <exception cref="DeserializationException">invalid pointer data or traversal limit exceeded</exception> internal void DecodePointer(int offset) { if (offset < 0) { throw new IndexOutOfRangeException(nameof(offset)); } WirePointer pointer = CurrentSegment[Offset + offset]; int derefCount = 0; do { if (pointer.IsNull) { this = default; return; } switch (pointer.Kind) { case PointerKind.Struct: Offset = checked (pointer.Offset + Offset + offset + 1); IncrementBytesTraversed(checked (8u * pointer.StructSize)); StructDataCount = pointer.StructDataCount; StructPtrCount = pointer.StructPtrCount; Kind = ObjectKind.Struct; Validate(); return; case PointerKind.List: Offset = checked (pointer.Offset + Offset + offset + 1); ListElementCount = pointer.ListElementCount; StructDataCount = 0; StructPtrCount = 0; switch (pointer.ListKind) { case ListKind.ListOfEmpty: // e.g. List(void) // the “traversal limit” should count a list of zero-sized elements as if each element were one word instead. IncrementBytesTraversed(checked (8u * (uint)ListElementCount)); Kind = ObjectKind.ListOfEmpty; break; case ListKind.ListOfBits: IncrementBytesTraversed(checked ((uint)ListElementCount + 7) / 8); Kind = ObjectKind.ListOfBits; break; case ListKind.ListOfBytes: IncrementBytesTraversed((uint)ListElementCount); Kind = ObjectKind.ListOfBytes; break; case ListKind.ListOfShorts: IncrementBytesTraversed(checked (2u * (uint)ListElementCount)); Kind = ObjectKind.ListOfShorts; break; case ListKind.ListOfInts: IncrementBytesTraversed(checked (4u * (uint)ListElementCount)); Kind = ObjectKind.ListOfInts; break; case ListKind.ListOfLongs: IncrementBytesTraversed(checked (8u * (uint)ListElementCount)); Kind = ObjectKind.ListOfLongs; break; case ListKind.ListOfPointers: IncrementBytesTraversed(checked (8u * (uint)ListElementCount)); Kind = ObjectKind.ListOfPointers; break; case ListKind.ListOfStructs: { WirePointer tag = CurrentSegment[Offset]; if (tag.Kind != PointerKind.Struct) { throw new DeserializationException("Unexpected: List of composites with non-struct type tag"); } IncrementBytesTraversed(checked (8u * (uint)pointer.ListElementCount + 8u)); ListElementCount = tag.ListOfStructsElementCount; StructDataCount = tag.StructDataCount; StructPtrCount = tag.StructPtrCount; Kind = ObjectKind.ListOfStructs; } break; default: throw new InvalidProgramException(); } Validate(); return; case PointerKind.Far: if (pointer.IsDoubleFar) { CurrentSegmentIndex = pointer.TargetSegmentIndex; Offset = 0; WirePointer pointer1 = CurrentSegment[pointer.LandingPadOffset]; if (pointer1.Kind != PointerKind.Far || pointer1.IsDoubleFar) { throw new DeserializationException("Error decoding double-far pointer: convention broken"); } WirePointer pointer2 = CurrentSegment[pointer.LandingPadOffset + 1]; if (pointer2.Kind == PointerKind.Far) { throw new DeserializationException("Error decoding double-far pointer: not followed by intra-segment pointer"); } CurrentSegmentIndex = pointer1.TargetSegmentIndex; Offset = 0; pointer = pointer2; offset = -1; } else { CurrentSegmentIndex = pointer.TargetSegmentIndex; Offset = 0; offset = pointer.LandingPadOffset; pointer = CurrentSegment[pointer.LandingPadOffset]; } continue; case PointerKind.Other: var tmp = Caps; this = default; Caps = tmp; Kind = ObjectKind.Capability; BytesTraversedOrData = pointer.CapabilityIndex; return; default: throw new InvalidProgramException(); } } while (++derefCount < SecurityOptions.RecursionLimit); throw new DeserializationException("Recursion limit reached while decoding a pointer"); }