📜 ⬆️ ⬇️

Damage to the stack in one of the NSString methods

I want to write about one strange crash with whom I understood at work.

Crash occurred stably when entering the folder with Korean characters. The problem was in the seemingly innocent code of the following form:

NSURLComponents* urlComp = [[NSURLComponents new] autorelease]; ... urlComp.path = path; urlComp.user = username; ... 


Falls when setting user - EXC_BAD_ACCESS inside the setter when sending objc_msgSend to someone. All variables are in order, nothing could break. At the same time, the crash is reproduced in the release configuration, but not in the release configuration. Cursing for the poor performance of the debugger in the release box, let's go look further.
')
Although the debugger often cannot print variables in the release, it is easy to see in which register, which variables should be in the disassembled listing, and the debugger is able to output objects normally (for example, po $ r0). It quickly becomes clear that the username is dead (in my case, the r10 register) - po $ r10 prints a number, not an object. Somewhat less quickly, it becomes clear that the value in the r10 register changed after setting the path.

Okay, let's look at what happens in the method "- [__ NSConcreteURLComponents setPath:]". Fortunately, it is small and it is clear that the register r10 flies when you call "- [NSString (NSURLUtilities) stringByAddingPercentEncodingWithAllowedCharacters:]" - i.e. when escaped transmitted path. This function is already large, and the customer will reject the head for its analysis, but at least we will look at the input-output

 0x2ca50aec: push.w {r8, r10, r11} 0x2ca50af0: sub.w sp, sp, #0x1020 0x2ca50af4: sub sp, #0x10 ... 0x2ca50e7a: add.w sp, sp, #0x1020 0x2ca50e7e: add sp, #0x10 0x2ca50e80: pop.w {r8, r10, r11} 

On entry, our r10 is saved to the stack, and on exit it is restored. The stack pointer (sp) is in the order that it was, then returned, but the stack contents themselves are not the same - the value of r10 was restored incorrectly. Thus, in the system function for percent encoding, there is a stack damage.

For clarity, I brought the code-sample to a clean test project:

 NSObject* obj1 = [[NSObject new] autorelease]; NSObject* obj2 = [[NSObject new] autorelease]; NSObject* obj3 = [[NSObject new] autorelease]; NSObject* obj4 = [[NSObject new] autorelease]; NSString* str = @"/Users/zaryanov/Movies/rootfolder/시티 오브 히어로 (City of Heroes)/로니 리 가드너 (1961년부터 2010년까지)는 1985 년에 살인죄로 사형을받은 유타 주에서 총살형 된 미국의 악당이었다. 1984 년에 그는 솔트 레이크 시티에서 강도 동안 바텐더를 살해.m4v"; NSLog(@"%s str %@", __func__, str); NSCharacterSet* charSet = [NSCharacterSet URLPathAllowedCharacterSet]; str = [str stringByAddingPercentEncodingWithAllowedCharacters:charSet]; NSLog(@"%s str %@", __func__, str); NSLog(@"%s obj1 %@ obj2 %@ obj3 %@ obj4 %@", __func__, obj1, obj2, obj3, obj4); 

In this case, the crash did not occur during the output to the log, as I expected, but in the most problematic function (escaping). The abort function worked in the __stack_chk_fail function - the fact is that the arm64 architecture was resolved in a clean test project, and there appears to be a stack check. If you leave only armv7, then a crash occurs when objects are output to the log, as I expected. In any case, the stack is damaged.

Further, the google by “stringByAddingPercentEncodingWithAllowedCharacters crash” gives some confirming results:

https://github.com/Alamofire/Alamofire/issues/206 - here, however, complain about the high memory consumption, but the function is the same;
https://gist.github.com/clowwindy/0d800f07a5e95e5c4dd0 - here is an example that tears down the stack completely, and not a couple of registers.

Actually, I took a logical solution from the first link provided - using CFURLCreateStringByAddingPercentEscapes, less convenient, but it works. Moreover, NSURLComponents can be set by themselves already zaekapelenny path.

The problem is reproduced on iOS 8.2, so it’s worth keeping a notch on the subcortex. As can be seen in my case, the crash may be slightly hidden and not obvious due to the implicit call of the problem function through another function. Well, with damage to the stack can get lucky in different ways, if it hooks only registers, then it may not immediately show up.

UPD:
Not finding such a bug in the openradar, I decided to post it. At the same time, I copied the example (not from the browser, but it can have the same result) and ... did not get a crash. It turned out that these hieroglyphs can be encoded in UTF-8 in different ways, and in one case it falls, but not in the other. So, the hieroglyph can be encoded as a composite of two elements (6 bytes will turn out - E1 84 89 E1 85 B5), or it can be encoded as already assembled (3 bytes will turn out - EC 8B 9C). In the second case, with such a line did not fall.

Bug posted - http://www.openradar.me/20404230 , there is also a link to the example in github, where the bug is reproduced.

Source: https://habr.com/ru/post/254545/


All Articles