• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file or at
6// https://developers.google.com/open-source/licenses/bsd
7
8#import "GPBCodedInputStream.h"
9#import "GPBCodedInputStream_PackagePrivate.h"
10
11#import "GPBDictionary.h"
12#import "GPBDictionary_PackagePrivate.h"
13#import "GPBMessage.h"
14#import "GPBMessage_PackagePrivate.h"
15#import "GPBUnknownFieldSet.h"
16#import "GPBUnknownFieldSet_PackagePrivate.h"
17#import "GPBUtilities.h"
18#import "GPBUtilities_PackagePrivate.h"
19#import "GPBWireFormat.h"
20
21// TODO: Consider using on other functions to reduce bloat when
22// some compiler optimizations are enabled.
23#define GPB_NOINLINE __attribute__((noinline))
24
25NSString *const GPBCodedInputStreamException = GPBNSStringifySymbol(GPBCodedInputStreamException);
26
27NSString *const GPBCodedInputStreamUnderlyingErrorKey =
28    GPBNSStringifySymbol(GPBCodedInputStreamUnderlyingErrorKey);
29
30NSString *const GPBCodedInputStreamErrorDomain =
31    GPBNSStringifySymbol(GPBCodedInputStreamErrorDomain);
32
33// Matching:
34// https://github.com/protocolbuffers/protobuf/blob/main/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L62
35//  private static final int DEFAULT_RECURSION_LIMIT = 100;
36// https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/io/coded_stream.cc#L86
37//  int CodedInputStream::default_recursion_limit_ = 100;
38static const NSUInteger kDefaultRecursionLimit = 100;
39
40GPB_NOINLINE
41void GPBRaiseStreamError(NSInteger code, NSString *reason) {
42  NSDictionary *errorInfo = nil;
43  if ([reason length]) {
44    errorInfo = @{GPBErrorReasonKey : reason};
45  }
46  NSError *error = [NSError errorWithDomain:GPBCodedInputStreamErrorDomain
47                                       code:code
48                                   userInfo:errorInfo];
49
50  NSDictionary *exceptionInfo = @{GPBCodedInputStreamUnderlyingErrorKey : error};
51  [[NSException exceptionWithName:GPBCodedInputStreamException reason:reason
52                         userInfo:exceptionInfo] raise];
53}
54
55GPB_INLINE void CheckRecursionLimit(GPBCodedInputStreamState *state) {
56  if (state->recursionDepth >= kDefaultRecursionLimit) {
57    GPBRaiseStreamError(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
58  }
59}
60
61GPB_INLINE void CheckFieldSize(uint64_t size) {
62  // Bytes and Strings have a max size of 2GB. And since messages are on the wire as bytes/length
63  // delimited, they also have a 2GB size limit. The C++ does the same sort of enforcement (see
64  // parse_context, delimited_message_util, message_lite, etc.).
65  // https://protobuf.dev/programming-guides/encoding/#cheat-sheet
66  if (size > 0x7fffffff) {
67    // TODO: Maybe a different error code for this, but adding one is a breaking
68    // change so reuse an existing one.
69    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSize, nil);
70  }
71}
72
73static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
74  size_t newSize = state->bufferPos + size;
75  if (newSize > state->bufferSize) {
76    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSize, nil);
77  }
78  if (newSize > state->currentLimit) {
79    // Fast forward to end of currentLimit;
80    state->bufferPos = state->currentLimit;
81    GPBRaiseStreamError(GPBCodedInputStreamErrorSubsectionLimitReached, nil);
82  }
83}
84
85static int8_t ReadRawByte(GPBCodedInputStreamState *state) {
86  CheckSize(state, sizeof(int8_t));
87  return ((int8_t *)state->bytes)[state->bufferPos++];
88}
89
90static int32_t ReadRawLittleEndian32(GPBCodedInputStreamState *state) {
91  CheckSize(state, sizeof(int32_t));
92  // Not using OSReadLittleInt32 because it has undocumented dependency
93  // on reads being aligned.
94  int32_t value;
95  memcpy(&value, state->bytes + state->bufferPos, sizeof(int32_t));
96  value = OSSwapLittleToHostInt32(value);
97  state->bufferPos += sizeof(int32_t);
98  return value;
99}
100
101static int64_t ReadRawLittleEndian64(GPBCodedInputStreamState *state) {
102  CheckSize(state, sizeof(int64_t));
103  // Not using OSReadLittleInt64 because it has undocumented dependency
104  // on reads being aligned.
105  int64_t value;
106  memcpy(&value, state->bytes + state->bufferPos, sizeof(int64_t));
107  value = OSSwapLittleToHostInt64(value);
108  state->bufferPos += sizeof(int64_t);
109  return value;
110}
111
112static int64_t ReadRawVarint64(GPBCodedInputStreamState *state) {
113  int32_t shift = 0;
114  int64_t result = 0;
115  while (shift < 64) {
116    int8_t b = ReadRawByte(state);
117    result |= (int64_t)((uint64_t)(b & 0x7F) << shift);
118    if ((b & 0x80) == 0) {
119      return result;
120    }
121    shift += 7;
122  }
123  GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidVarInt, @"Invalid VarInt64");
124  return 0;
125}
126
127static int32_t ReadRawVarint32(GPBCodedInputStreamState *state) {
128  return (int32_t)ReadRawVarint64(state);
129}
130
131static void SkipRawData(GPBCodedInputStreamState *state, size_t size) {
132  CheckSize(state, size);
133  state->bufferPos += size;
134}
135
136double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state) {
137  int64_t value = ReadRawLittleEndian64(state);
138  return GPBConvertInt64ToDouble(value);
139}
140
141float GPBCodedInputStreamReadFloat(GPBCodedInputStreamState *state) {
142  int32_t value = ReadRawLittleEndian32(state);
143  return GPBConvertInt32ToFloat(value);
144}
145
146uint64_t GPBCodedInputStreamReadUInt64(GPBCodedInputStreamState *state) {
147  uint64_t value = ReadRawVarint64(state);
148  return value;
149}
150
151uint32_t GPBCodedInputStreamReadUInt32(GPBCodedInputStreamState *state) {
152  uint32_t value = ReadRawVarint32(state);
153  return value;
154}
155
156int64_t GPBCodedInputStreamReadInt64(GPBCodedInputStreamState *state) {
157  int64_t value = ReadRawVarint64(state);
158  return value;
159}
160
161int32_t GPBCodedInputStreamReadInt32(GPBCodedInputStreamState *state) {
162  int32_t value = ReadRawVarint32(state);
163  return value;
164}
165
166uint64_t GPBCodedInputStreamReadFixed64(GPBCodedInputStreamState *state) {
167  uint64_t value = ReadRawLittleEndian64(state);
168  return value;
169}
170
171uint32_t GPBCodedInputStreamReadFixed32(GPBCodedInputStreamState *state) {
172  uint32_t value = ReadRawLittleEndian32(state);
173  return value;
174}
175
176int32_t GPBCodedInputStreamReadEnum(GPBCodedInputStreamState *state) {
177  int32_t value = ReadRawVarint32(state);
178  return value;
179}
180
181int32_t GPBCodedInputStreamReadSFixed32(GPBCodedInputStreamState *state) {
182  int32_t value = ReadRawLittleEndian32(state);
183  return value;
184}
185
186int64_t GPBCodedInputStreamReadSFixed64(GPBCodedInputStreamState *state) {
187  int64_t value = ReadRawLittleEndian64(state);
188  return value;
189}
190
191int32_t GPBCodedInputStreamReadSInt32(GPBCodedInputStreamState *state) {
192  int32_t value = GPBDecodeZigZag32(ReadRawVarint32(state));
193  return value;
194}
195
196int64_t GPBCodedInputStreamReadSInt64(GPBCodedInputStreamState *state) {
197  int64_t value = GPBDecodeZigZag64(ReadRawVarint64(state));
198  return value;
199}
200
201BOOL GPBCodedInputStreamReadBool(GPBCodedInputStreamState *state) {
202  return ReadRawVarint64(state) != 0;
203}
204
205int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state) {
206  if (GPBCodedInputStreamIsAtEnd(state)) {
207    state->lastTag = 0;
208    return 0;
209  }
210
211  state->lastTag = ReadRawVarint32(state);
212  // Tags have to include a valid wireformat.
213  if (!GPBWireFormatIsValidTag(state->lastTag)) {
214    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Invalid wireformat in tag.");
215  }
216  // Zero is not a valid field number.
217  if (GPBWireFormatGetTagFieldNumber(state->lastTag) == 0) {
218    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag,
219                        @"A zero field number on the wire is invalid.");
220  }
221  return state->lastTag;
222}
223
224NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state) {
225  uint64_t size = GPBCodedInputStreamReadUInt64(state);
226  CheckFieldSize(size);
227  NSUInteger ns_size = (NSUInteger)size;
228  NSString *result;
229  if (size == 0) {
230    result = @"";
231  } else {
232    size_t size2 = (size_t)size;  // Cast safe on 32bit because of CheckFieldSize() above.
233    CheckSize(state, size2);
234    result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
235                                      length:ns_size
236                                    encoding:NSUTF8StringEncoding];
237    state->bufferPos += size;
238    if (!result) {
239#ifdef DEBUG
240      // https://developers.google.com/protocol-buffers/docs/proto#scalar
241      NSLog(@"UTF-8 failure, is some field type 'string' when it should be "
242            @"'bytes'?");
243#endif
244      GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidUTF8, nil);
245    }
246  }
247  return result;
248}
249
250NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) {
251  uint64_t size = GPBCodedInputStreamReadUInt64(state);
252  CheckFieldSize(size);
253  size_t size2 = (size_t)size;  // Cast safe on 32bit because of CheckFieldSize() above.
254  CheckSize(state, size2);
255  NSUInteger ns_size = (NSUInteger)size;
256  NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos length:ns_size];
257  state->bufferPos += size;
258  return result;
259}
260
261NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(GPBCodedInputStreamState *state) {
262  uint64_t size = GPBCodedInputStreamReadUInt64(state);
263  CheckFieldSize(size);
264  size_t size2 = (size_t)size;  // Cast safe on 32bit because of CheckFieldSize() above.
265  CheckSize(state, size2);
266  NSUInteger ns_size = (NSUInteger)size;
267  // Cast is safe because freeWhenDone is NO.
268  NSData *result = [[NSData alloc] initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos)
269                                                length:ns_size
270                                          freeWhenDone:NO];
271  state->bufferPos += size;
272  return result;
273}
274
275static void SkipToEndGroupInternal(GPBCodedInputStreamState *state, uint32_t endGroupTag) {
276  CheckRecursionLimit(state);
277  ++state->recursionDepth;
278  while (YES) {
279    uint32_t tag = GPBCodedInputStreamReadTag(state);
280    if (tag == endGroupTag || tag == 0) {
281      GPBCodedInputStreamCheckLastTagWas(state, endGroupTag);  // Will fail for end of input.
282      --state->recursionDepth;
283      return;
284    }
285    switch (GPBWireFormatGetTagWireType(tag)) {
286      case GPBWireFormatVarint:
287        (void)ReadRawVarint64(state);
288        break;
289      case GPBWireFormatFixed64:
290        SkipRawData(state, sizeof(uint64_t));
291        break;
292      case GPBWireFormatLengthDelimited: {
293        uint64_t size = ReadRawVarint64(state);
294        CheckFieldSize(size);
295        size_t size2 = (size_t)size;  // Cast safe on 32bit because of CheckFieldSize() above.
296        SkipRawData(state, size2);
297        break;
298      }
299      case GPBWireFormatStartGroup:
300        SkipToEndGroupInternal(state, GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag),
301                                                           GPBWireFormatEndGroup));
302        break;
303      case GPBWireFormatEndGroup:
304        GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unmatched end group");
305        break;
306      case GPBWireFormatFixed32:
307        SkipRawData(state, sizeof(uint32_t));
308        break;
309    }
310  }
311}
312
313// This doesn't include the start group, but it collects all bytes until the end group including
314// the end group tag.
315NSData *GPBCodedInputStreamReadRetainedBytesToEndGroupNoCopy(GPBCodedInputStreamState *state,
316                                                             int32_t fieldNumber) {
317  // Better have just read the start of the group.
318  GPBCodedInputStreamCheckLastTagWas(state,
319                                     GPBWireFormatMakeTag(fieldNumber, GPBWireFormatStartGroup));
320  const uint8_t *start = state->bytes + state->bufferPos;
321  SkipToEndGroupInternal(state, GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
322  // This will be after the end group tag.
323  const uint8_t *end = state->bytes + state->bufferPos;
324  return [[NSData alloc] initWithBytesNoCopy:(void *)start
325                                      length:(NSUInteger)(end - start)
326                                freeWhenDone:NO];
327}
328
329size_t GPBCodedInputStreamPushLimit(GPBCodedInputStreamState *state, size_t byteLimit) {
330  byteLimit += state->bufferPos;
331  size_t oldLimit = state->currentLimit;
332  if (byteLimit > oldLimit) {
333    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidSubsectionLimit, nil);
334  }
335  state->currentLimit = byteLimit;
336  return oldLimit;
337}
338
339void GPBCodedInputStreamPopLimit(GPBCodedInputStreamState *state, size_t oldLimit) {
340  state->currentLimit = oldLimit;
341}
342
343size_t GPBCodedInputStreamBytesUntilLimit(GPBCodedInputStreamState *state) {
344  return state->currentLimit - state->bufferPos;
345}
346
347BOOL GPBCodedInputStreamIsAtEnd(GPBCodedInputStreamState *state) {
348  return (state->bufferPos == state->bufferSize) || (state->bufferPos == state->currentLimit);
349}
350
351void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, int32_t value) {
352  if (state->lastTag != value) {
353    GPBRaiseStreamError(GPBCodedInputStreamErrorInvalidTag, @"Unexpected tag read");
354  }
355}
356
357@implementation GPBCodedInputStream
358
359+ (instancetype)streamWithData:(NSData *)data {
360  return [[[self alloc] initWithData:data] autorelease];
361}
362
363- (instancetype)initWithData:(NSData *)data {
364  if ((self = [super init])) {
365#ifdef DEBUG
366    NSCAssert([self class] == [GPBCodedInputStream class],
367              @"Subclassing of GPBCodedInputStream is not allowed.");
368#endif
369    buffer_ = [data retain];
370    state_.bytes = (const uint8_t *)[data bytes];
371    state_.bufferSize = [data length];
372    state_.currentLimit = state_.bufferSize;
373  }
374  return self;
375}
376
377- (void)dealloc {
378  [buffer_ release];
379  [super dealloc];
380}
381
382// Direct access is use for speed, to avoid even internally declaring things
383// read/write, etc. The warning is enabled in the project to ensure code calling
384// protos can turn on -Wdirect-ivar-access without issues.
385#pragma clang diagnostic push
386#pragma clang diagnostic ignored "-Wdirect-ivar-access"
387
388- (int32_t)readTag {
389  return GPBCodedInputStreamReadTag(&state_);
390}
391
392- (void)checkLastTagWas:(int32_t)value {
393  GPBCodedInputStreamCheckLastTagWas(&state_, value);
394}
395
396- (BOOL)skipField:(int32_t)tag {
397  NSAssert(GPBWireFormatIsValidTag(tag), @"Invalid tag");
398  switch (GPBWireFormatGetTagWireType(tag)) {
399    case GPBWireFormatVarint:
400      GPBCodedInputStreamReadInt32(&state_);
401      return YES;
402    case GPBWireFormatFixed64:
403      SkipRawData(&state_, sizeof(int64_t));
404      return YES;
405    case GPBWireFormatLengthDelimited: {
406      uint64_t size = GPBCodedInputStreamReadUInt64(&state_);
407      CheckFieldSize(size);
408      size_t size2 = (size_t)size;  // Cast safe on 32bit because of CheckFieldSize() above.
409      SkipRawData(&state_, size2);
410      return YES;
411    }
412    case GPBWireFormatStartGroup:
413      [self skipMessage];
414      GPBCodedInputStreamCheckLastTagWas(
415          &state_,
416          GPBWireFormatMakeTag(GPBWireFormatGetTagFieldNumber(tag), GPBWireFormatEndGroup));
417      return YES;
418    case GPBWireFormatEndGroup:
419      return NO;
420    case GPBWireFormatFixed32:
421      SkipRawData(&state_, sizeof(int32_t));
422      return YES;
423  }
424}
425
426- (void)skipMessage {
427  while (YES) {
428    int32_t tag = GPBCodedInputStreamReadTag(&state_);
429    if (tag == 0 || ![self skipField:tag]) {
430      return;
431    }
432  }
433}
434
435- (BOOL)isAtEnd {
436  return GPBCodedInputStreamIsAtEnd(&state_);
437}
438
439- (size_t)position {
440  return state_.bufferPos;
441}
442
443- (size_t)pushLimit:(size_t)byteLimit {
444  return GPBCodedInputStreamPushLimit(&state_, byteLimit);
445}
446
447- (void)popLimit:(size_t)oldLimit {
448  GPBCodedInputStreamPopLimit(&state_, oldLimit);
449}
450
451- (size_t)bytesUntilLimit {
452  return GPBCodedInputStreamBytesUntilLimit(&state_);
453}
454
455- (double)readDouble {
456  return GPBCodedInputStreamReadDouble(&state_);
457}
458
459- (float)readFloat {
460  return GPBCodedInputStreamReadFloat(&state_);
461}
462
463- (uint64_t)readUInt64 {
464  return GPBCodedInputStreamReadUInt64(&state_);
465}
466
467- (int64_t)readInt64 {
468  return GPBCodedInputStreamReadInt64(&state_);
469}
470
471- (int32_t)readInt32 {
472  return GPBCodedInputStreamReadInt32(&state_);
473}
474
475- (uint64_t)readFixed64 {
476  return GPBCodedInputStreamReadFixed64(&state_);
477}
478
479- (uint32_t)readFixed32 {
480  return GPBCodedInputStreamReadFixed32(&state_);
481}
482
483- (BOOL)readBool {
484  return GPBCodedInputStreamReadBool(&state_);
485}
486
487- (NSString *)readString {
488  return [GPBCodedInputStreamReadRetainedString(&state_) autorelease];
489}
490
491- (void)readGroup:(int32_t)fieldNumber
492              message:(GPBMessage *)message
493    extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
494  CheckRecursionLimit(&state_);
495  ++state_.recursionDepth;
496  [message mergeFromCodedInputStream:self
497                   extensionRegistry:extensionRegistry
498                           endingTag:GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup)];
499  --state_.recursionDepth;
500}
501
502#pragma clang diagnostic push
503#pragma clang diagnostic ignored "-Wdeprecated-declarations"
504- (void)readUnknownGroup:(int32_t)fieldNumber message:(GPBUnknownFieldSet *)message {
505  CheckRecursionLimit(&state_);
506  ++state_.recursionDepth;
507  [message mergeFromCodedInputStream:self];
508  GPBCodedInputStreamCheckLastTagWas(&state_,
509                                     GPBWireFormatMakeTag(fieldNumber, GPBWireFormatEndGroup));
510  --state_.recursionDepth;
511}
512#pragma clang diagnostic pop
513
514- (void)readMessage:(GPBMessage *)message
515    extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
516  CheckRecursionLimit(&state_);
517  uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
518  CheckFieldSize(length);
519  size_t length2 = (size_t)length;  // Cast safe on 32bit because of CheckFieldSize() above.
520  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length2);
521  ++state_.recursionDepth;
522  [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry endingTag:0];
523  --state_.recursionDepth;
524  GPBCodedInputStreamPopLimit(&state_, oldLimit);
525}
526
527- (void)readMapEntry:(id)mapDictionary
528    extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
529                field:(GPBFieldDescriptor *)field
530        parentMessage:(GPBMessage *)parentMessage {
531  CheckRecursionLimit(&state_);
532  uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
533  CheckFieldSize(length);
534  size_t length2 = (size_t)length;  // Cast safe on 32bit because of CheckFieldSize() above.
535  size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length2);
536  ++state_.recursionDepth;
537  GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field, parentMessage);
538  GPBCodedInputStreamCheckLastTagWas(&state_, 0);
539  --state_.recursionDepth;
540  GPBCodedInputStreamPopLimit(&state_, oldLimit);
541}
542
543- (NSData *)readBytes {
544  return [GPBCodedInputStreamReadRetainedBytes(&state_) autorelease];
545}
546
547- (uint32_t)readUInt32 {
548  return GPBCodedInputStreamReadUInt32(&state_);
549}
550
551- (int32_t)readEnum {
552  return GPBCodedInputStreamReadEnum(&state_);
553}
554
555- (int32_t)readSFixed32 {
556  return GPBCodedInputStreamReadSFixed32(&state_);
557}
558
559- (int64_t)readSFixed64 {
560  return GPBCodedInputStreamReadSFixed64(&state_);
561}
562
563- (int32_t)readSInt32 {
564  return GPBCodedInputStreamReadSInt32(&state_);
565}
566
567- (int64_t)readSInt64 {
568  return GPBCodedInputStreamReadSInt64(&state_);
569}
570
571#pragma clang diagnostic pop
572
573@end
574