1

I'm trying to find a buffer overflow sample code in Objective-C but the only samples I found are written in C (without "bracket style").

I understand that Objective-C is based on C, but are there any chance of getting buffer overflows (or any kind of overflow) using only Objective-C ?

Would you have a sample code to demonstrate it ?

Thanks in advance.

John Kravicz
  • 142
  • 7
  • Some libraries may expose Objective-C bindings, which are just thin wrappers for native code. Those wrappers may or may not sanitize input correctly, potentially leading to buffer overflows, even if you only called Objective-C code. –  Sep 26 '19 at 09:10
  • Does it mean that there is no possibility to misuse the _Objective-C native functions_ in order to implement a buffer overflow ? – John Kravicz Sep 26 '19 at 09:44
  • No, you misunderstood me greatly here. My example is a way how you can get a buffer overflow calling only Objective-C. I would *never* go as far as to say some set of functions is safe for every possible combination of inputs. –  Sep 26 '19 at 09:50

2 Answers2

4

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
CBHacking
  • 40,303
  • 3
  • 74
  • 98
  • Nit: standard C does not allow pointer arithmetic on `void*`, or other incomplete`*` or function`*` (or qualified versions). _GCC_ does allow `void*` and function`*` treated as having stride 1, as an extension. If your implementation has `[u]intptr_t` _and_ uses byte addresses -- both common but neither required -- you can do the arithmetic in that type. – dave_thompson_085 Sep 27 '19 at 04:38
  • @dave_thompson_085 Legit; I forgot that was an extension, and don't know if it's supported in ObjC compilers either. Switched the void* to a char*; the point was just that it's "dereference the address index*elementsize bytes from the head of the array". The point about possibly not using byte addresses is also valid, though. – CBHacking Sep 27 '19 at 17:20
0

I can't think of any uncontrived way of being able to cause a buffer overflow without falling back to C functions.

SonarSource has some good rules for Objective-C source-code auditing, and their only rule in this area is to look for C functions: https://rules.sonarsource.com/objective-c/RSPEC-1081

Additionally... buffer overflows are still possible in SWIFT technically, due to the few functions which aptly contain the name 'unsafe'. My understanding is that these allow for manual memory management in a C-like way.

hiburn8
  • 441
  • 2
  • 11