美文网首页
一个有意思的栈溢出crash

一个有意思的栈溢出crash

作者: 思考着自己的未来 | 来源:发表于2019-10-04 12:27 被阅读0次

问题引出

最近在复习操作系统相关知识的时候,回忆起之前在某个版本遇到的Android离奇crash,记得当时这个crash在灰度期间造成的影响面不小,占到了整体crash率的10%,虽然crash堆栈能定位到是哪个位置,但是很多Android手机都难以复现,经过不懈努力,发现小米6能稳定复现这个crash,于是我们召集了小组同事到会议室集体应对这个问题,然而当时四、五个工程师对这个问题束手无策,也只能通过代码回滚的方式,去确认是哪一次提交导致的问题,不停的回滚代码,不停的打包,然而提交次数实在太多,导致一直没有定位到问题。

最后我拿着这个手机带回家,我就不信必现的问题还跟不出来,跟到晚上2点左右,突然发现自己这个版本提交的某个代码会作用到这个crash堆栈,那个提交我只是在某个struct中增加了一个变量,这个变量的大小为1kb左右,于是我把这个变量注释掉,果然crash不再出现,于是我不停的调整这个变量的大小,发现调整到256字节以下就不会有问题,大于256字节有可能出现crash,当时我自己也是百思不得其解,不过毕竟crash解决了,我们能正常发版,也就把这个事给忘了。

到最近,我突然发现这个crash可能和线程栈溢出有极大的关系。

问题定位

结构体占据的内存变大,就导致crash,内存变小,就不会有问题。沿着这个线索,我发现在某个函数中,定义了一个该结构体的局部变量,而且这个调用栈的深度达到了18层,于是更加确信是该局部变量过大且沿途的调用栈过大,导致变量在压栈的过程中,栈溢出了。

在linux上,可以很容易看到线程栈的默认大小,我的跳板机输出的线程栈大小为10MB

ulimit -s //10240=10MB

然而对于移动端,会稍有不同,因为移动端分为UI绘制主线程和子线程,主线程占的比重和资源自然要多些。我查阅了一番资料后,发现对于Android来说,不同的版本可能不一样,但是基本是主线程栈默认为8MB,子线程栈稍微小于1MB;对于IOS来说,这个默认值就更小了,IOS主线程栈默认为1MB,其他线程为512KB。

而我们的So起的线程,并非主线程,因此通常只有1MB左右的栈空间,那么我们怎么验证crash那一次的函数调用栈真的是栈溢出导致的呢,其实很简单,只需要打印两个地址即可:

线程执行的第一个函数,函数入参的压栈地址
该线程执行的最深层函数,函数最后声明的局部变量的地址
我使用这个方法,打印出两个地址分别为:0x000000016d09eee0和0x000000016cfac3bc,因为栈是向下增长的,用前一个地址减去后一个地址得到差值为994084字节,很显然已经接近1MB,然而我定位的可能还不是最深层的调用,因此这个调用栈,增大某个局部变量的大小,随时都有可能出现栈溢出的风险。

问题总结

通过分析这个crash的调用栈发现,这18层调用栈中,前几层都出现了较大的局部变量拷贝的问题,达到50kb左右,导致随着调用层次的加深,局部变量和函数参数越来越多,占据的栈空间也越来越大,随时都可能存在栈溢出的风险。问题定之后,解决方法也比较简单:

  • 创建线程时,扩大线程栈的默认大小
  • C++编程中,将过大的局部变量从栈空间转到堆空间
  • 函数参数传递过程中,对于占用较大内存的变量,避免值拷贝,使用引用或指针
  • 尽量使用循环代替递归
#include <pthread.h>
int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

参考

Android线程堆栈大小:https://zhuanlan.zhihu.com/p/33562383
IOS线程堆栈大小:https://www.jianshu.com/p/51b9139442b5
IOS栈溢出crash:http://initlife.com/blog/2015/10/28/iosli-de-zhan-xian-zhi-yin-fa-de-crash/
线程栈大小设置和获取:http://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_attr_getstacksize.html

相关文章

  • 一个有意思的栈溢出crash

    问题引出 最近在复习操作系统相关知识的时候,回忆起之前在某个版本遇到的Android离奇crash,记得当时这个c...

  • Method Swizzle的危机

    在同时使用RAC和Aspects的时候,遇到了一个Crash,栈溢出了。 看了一下,是之前在项目中使用了RAC的r...

  • 2019-04-05一个月的约定

    一个月的约定快要到了,疯狂补栈溢出知识,偷看大佬们博客,希望学到点什么。 栈溢出学习网站 CTF Wiki 栈溢出...

  • 浅说iOS为什么会上栈溢出

    简介 本文介绍了如下内容 栈的概念 为什么会发生栈溢出 栈溢出的几种栗子 怎么预防和发现栈溢出。 什么是栈? 从数...

  • 栈溢出简易指南

    栈 pwn 主题: 基本栈溢出 针对缓存区溢出防护的对策 shellcode 栈溢出的最终目的是执行shellco...

  • JVM

    1、一般什么情况会发生栈溢出、堆溢出 栈溢出(StackOverflowError) 1、栈是线程私有的,他的生命...

  • Android 7.1.1 系统使用Toast 可能出现的Bad

    一. 情况简介 最近在crash 平台上出现了一个BadTokenException 的crash 从崩溃栈中可以...

  • Canary机制及绕过策略-格式化字符串漏洞泄露Canary

    Canary主要用于防护栈溢出攻击。我们知道,在32位系统上,对于栈溢出漏洞,攻击者通常是通过溢出栈缓冲区,覆盖栈...

  • 2019-04-06 递归函数

    栈溢出

  • canary骚操作一览

    canary是栈溢出的防护机制,一般是位于ebp的上方,用于检测栈帧是否有溢出状态,用图形表示: 因为一个栈帧往往...

网友评论

      本文标题:一个有意思的栈溢出crash

      本文链接:https://www.haomeiwen.com/subject/vbdoyctx.html