Edit: Added code sample
Objective-C is a superset of C; anything you can do with C can be done with Objective-C and that includes lots of things you probably don't want to do (like buffer overflows). Taking a .c source file, changing the extension to .m, and compiling it with an Objective-C compiler will not prevent buffer overflows.
Additionally, the Objective-C language itself is not bounds-checked. Static buffers (as used in C; the things you get from int foo[16]
or malloc(100)
) are still the only inherently-supported index-accessible data structure in Objective-C, and there's nothing preventing you from reading or writing outside the bounds of one.
However, the vast majority of Objective-C code is written using a "kit" or "framework" (such as Cocoa or GNUstep) that provides a library of classes, including one or more array classes (such as NSArray
), and those classes may be bounds-checked. While something like NSArray
must use an unchecked memory buffer as its backing store (either directly or by wrapping some other array class), it need not expose that buffer directly. Instead, it supports messages such as objectAtIndex:
which invoke methods that perform bounds checking before reading or writing from that buffer.
Even these library classes' interfaces may interact with C-style memory buffers, in which case they typically rely on you to ensure the buffer's size is correct. Getting it wrong can still result in a buffer overread or overflow, either immediately inside of the function where you passed the wrong size parameter, or later when you try to access a location that is inside the size you specified but outside the buffer's actual size. For some examples, see the NSArray methods arrayWithObjects:count:
and getObjects:range:
, which require the programmer to specify a C-style array and either also supply its size or at least know that its size is sufficient. These functions can cause a overread or overflow, respectively.
Apple also recently (compared to the language's 35-year history) added support for C-style array access to collections such as NSArray
, but it's just syntactic sugar for the existing ways to interact with collections. You can use myNSArray[2]
to retrieve the third element of the NSArray just as you could use array[2]
to retrieve the third element of a C-style array, but the immediate behavior under the hood is completely different. Indexing with a C-style array is just pointer arithmetic (array[7]
is exactly equivalent to *(array+7)
, itself equivalent - at least on byte-addressable platforms like x86/x64 and assuming "array" is a pointer-to-int - to *((int*)(((uintptr_t)array)+(sizeof(int)*7)))
or *((int*)(((char*)array)+(sizeof(int)*7)))
, and can often be entirely implemented in a single line of x86 assembly. By comparison, myNSArray[7]
is just syntactic sugar for [myNSArray objectAtIndex:7]
and is a method call. It is in that method that the bounds check is implemented, the exception is raised if out-of-bounds, the indexing into the C-style buffer backing the NSArray happens, and finally the retrieved object is returned.
So, in summary, if you:
- use only bounds-checked collections in your Objective-C and let the library handle all the access to their C-style buffer backing stores
- never personally do any pointer arithmetic or call any function to do so when the function / receiver cannot tell or does not enforce the bounds of the pointed-to buffer
- never directly or indirectly call any functions that take C-style arrays and expect you to provide their bounds and/or index
then you can mostly-safely pretend that Objective-C is unaffected by buffer overflows. (I say "mostly-safely" because there may be bugs in the libraries you use, which could result in a bounds check being missed or performed incorrectly.) Anytime you are not following those rules, or handling data tainted by not following those rules, you're at risk... even if you're using "bracket style" messages to Objective-C receivers.
// Code example for a buffer overflow in Objective-C.
// NSArray with 6 Objective-C elements
NSArray *myArray = @[ @"Buffer", @"overflows", @"can", @"happen", @"in", @"Objective-C" ];
// NSRange within the bounds of the array
NSRange myRange = NSMakeRange(1, 4);
// Buffer with room for 2 Objective-C objects
id buffer[2];
// Use an NSArray method with "bracket style" syntax to copy data to the buffer
[myArray getObjects:buffer range:myRange];
// Boom