0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux程序內(nèi)存越界定位分析

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-13 11:30 ? 次閱讀

問題描述:最近在工作中遇到這樣一個(gè)奇葩問題,程序里需使用一個(gè).so庫,同份源碼用我電腦編譯的庫放到程序使用出現(xiàn)各種異常問題,其他同事編譯出來的沒問題。剛開始以為是編譯方式有問題,思來想去發(fā)現(xiàn)并不是。經(jīng)分析發(fā)現(xiàn)是庫源代碼里一全局?jǐn)?shù)組內(nèi)存地址大面積越界到其他全局?jǐn)?shù)組了。

問題現(xiàn)象:現(xiàn)象為觸發(fā)某個(gè)業(yè)務(wù)條件,將導(dǎo)致程序邏輯運(yùn)行不正常,異常log如下圖,可看出“g_s32MaxFd”變量的值(文件句柄)被置0,正常情況應(yīng)該是大于0,所以此時(shí)導(dǎo)致整個(gè)業(yè)務(wù)運(yùn)行異常。

圖片

初步分析肯定是其他地方對(duì)變量“g_s32MaxFd”有賦值才會(huì)導(dǎo)致值為0。那么到底是代碼正常邏輯語句操作還是代碼內(nèi)存越界引起“g_s32MaxFd”值為0呢?這個(gè)倒好定位,只需要搜索下“g_s32MaxFd”變量在代碼哪些地方有使用就知道了,得出結(jié)論是代碼內(nèi)存越界這種情況導(dǎo)致。

一:開始定位內(nèi)存越界處

【1】定位內(nèi)存越界處,因程序并沒有因?yàn)閮?nèi)存越界而引發(fā)segment fault退出,所以準(zhǔn)備使用Linux中mprotect()函數(shù)來設(shè)置指定內(nèi)存區(qū)域的保護(hù)屬性為只讀,故意使程序引發(fā)segment fault退出從而產(chǎn)生core dumped文件來定位問題點(diǎn)。

分析下面問題前最好先熟悉下mprotect()函數(shù)

思路:使用mprotect()函數(shù)對(duì)被踩變量“g_s32MaxFd”內(nèi)存地址設(shè)為只讀屬性,由于mprotect()函數(shù)的局限性(保護(hù)屬性區(qū)域的起始地址必須為操作系統(tǒng)一個(gè)頁大小的整數(shù)倍),結(jié)合實(shí)際情況多樣性,分析情況如下表述:

1、當(dāng)“g_s32MaxFd”數(shù)組起始地址剛好是頁大小整數(shù)倍時(shí),此時(shí)只需要將數(shù)組起始地址設(shè)置為mprotect()函數(shù)保護(hù)屬性為只讀的起始地址即可,但需要注意一點(diǎn),當(dāng)被保護(hù)地址區(qū)域被程序正常數(shù)據(jù)結(jié)構(gòu)進(jìn)行訪問時(shí),也會(huì)引發(fā)segment fault退出(簡而言之就是當(dāng)數(shù)組“g_s32MaxFd”內(nèi)存地址被設(shè)置為只讀后,如果是程序正常使用時(shí)也會(huì)引發(fā)段錯(cuò)誤退出),這種情況就無法辨別是程序正常使用還是內(nèi)存越界處使用,會(huì)影響分析真正的問題點(diǎn)。

解決方法:可利用GNU編譯器對(duì).bss地址分配特性(具體特性自行查閱其他資料),在“g_s32MaxFd”數(shù)組地址處定義一個(gè)為頁大小整數(shù)倍大小的“g_debug_place”數(shù)組,這就相當(dāng)于新增的“g_debug_place”數(shù)組占用之前“g_s32MaxFd”數(shù)組的地址。如下圖所示在“Var5”和“g_s32MaxFd”之間定義一個(gè)動(dòng)態(tài)數(shù)組“g_debug_place”,大小最好是頁大小整數(shù)倍(如果小于一個(gè)頁大小會(huì)導(dǎo)致鎖定的區(qū)域越界到“g_s32MaxFd”地址,問題得不到解決),這樣既可以保證新增的“g_debug_place”數(shù)組變量只在內(nèi)存越界的地方才會(huì)被訪問而且數(shù)組大小也滿足mprotect()函數(shù)參數(shù)長度的取值要求(頁大小整數(shù)倍)。

圖片

2、 當(dāng)“g_s32MaxFd”數(shù)組起始地址不是頁大小整數(shù)倍時(shí),要結(jié)合上面第1種方法后還需要計(jì)算出大于且最靠近“g_debug_place”數(shù)組起始地址的頁大小整數(shù)倍地址??商子霉剑?/p>

設(shè)置保護(hù)屬性起始地址=被踩內(nèi)存變量起始地址+(頁大小-(被踩內(nèi)存變量起始地址%頁大小)) 注意:(被踩內(nèi)存變量起始地址%頁大小)等于0時(shí)不適用以上公式,也就是被踩內(nèi)存變量起始地址是頁大小整數(shù)倍情況下

假設(shè)“g_debug_place”數(shù)組起始地址為0x7fd8985bf8c0代入公式可得設(shè)置保護(hù)屬性起始地址為0x7fd8985c0000 ,理論上只需要將地址0x7fd8985c0000設(shè)置為mprotect()函數(shù)保護(hù)屬性為只讀的起始地址即可,但需要注意的是此時(shí)的0x7fd8985c0000地址并不是“g_debug_place”數(shù)組起始地址,由上面公式可知這個(gè)地址是為了滿足mprotect()函數(shù)的局限性而計(jì)算出來的地址。

解決方法:可通過在.bss段(之所以強(qiáng)調(diào).bss段是因?yàn)槲覍?shí)際出現(xiàn)問題的變量就是未初始化的全局?jǐn)?shù)組變量)首個(gè)變量地址前增加動(dòng)態(tài)數(shù)組來改變內(nèi)存分配解決。舉個(gè)例子,就好比是排隊(duì),本來小明是排第六個(gè),突然在隊(duì)伍最前面插一個(gè)小紅進(jìn)來,小明就排在第七了,而小明前面之前那五個(gè)人的順序還是不變。而這個(gè)第七就是我們程序里要的那個(gè)0x7fd8985c0000地址。

下圖藍(lán)色區(qū)域?yàn)樾略鰟?dòng)態(tài)數(shù)組(插隊(duì)小紅),大小為0x740字節(jié)。增加后可使“g_debug_place”數(shù)組起始地址為0x7fd8985c0000(小明第七的位置),這時(shí)將0x7fd8985c0000地址作為mprotect()函數(shù)保護(hù)屬性為只讀的起始地址就可以了,接下來就可以復(fù)現(xiàn)問題等著程序內(nèi)存越界產(chǎn)生段錯(cuò)誤退出吧。

注意:如果增加動(dòng)態(tài)數(shù)組后并沒有直觀發(fā)現(xiàn)內(nèi)存越界時(shí),這可能是由于內(nèi)存越界的字節(jié)數(shù)太小(可能只踩到一個(gè)字節(jié)或幾個(gè)字節(jié)),導(dǎo)致調(diào)整過后的內(nèi)存地址剛好踩到一個(gè)未使用的地址,這時(shí)需要微調(diào)動(dòng)態(tài)數(shù)組大小來保證地址間隔及分配順序不變,具體問題具體分析。我是沒有出現(xiàn)這種情況,只是覺得通過這種方法分析可能會(huì)存在此風(fēng)險(xiǎn),如果有小伙伴遇到可以留言探討。

圖片

bss段變量地址結(jié)構(gòu)分布簡要展示如下圖(展示的是測(cè)試代碼,非實(shí)際工程代碼):

圖片

【2】gdb分析core文件,編譯可執(zhí)行程序時(shí)編譯選項(xiàng)需加-g參數(shù),不要strip優(yōu)化,否則可能會(huì)導(dǎo)致調(diào)試信息不是很完整。

檢查core dumped是否打開

/home # ulimit -c
0
/home # ulimit -c unlimited
/home # ulimit -c
unlimited

如果找不到ulimit命令,可以用busybox sh -c 'ulimit -a’指令測(cè)試ulimit是否存在,(ulimit是busybox的內(nèi)置命令,往往我們想使用tab鍵快捷調(diào)用ulimit時(shí)可能不會(huì)彈出)有如下log輸出證明命令存在,后續(xù)直接執(zhí)行ulimit -c unlimited,不要再執(zhí)行busybox sh -c ‘ulimit -c unlimited’,這樣是打不開core的,我就這么傻的操作過,當(dāng)時(shí)還以為內(nèi)核沒有打開這個(gè)功能。

/home # busybox sh -c 'ulimit -a'
-f: file size (blocks)             unlimited
-t: cpu time (seconds)             unlimited
-d: data seg size (kb)             unlimited
-s: stack size (kb)                8192
-c: core file size (blocks)        unlimited
-m: resident set size (kb)         unlimited
-l: locked memory (kb)             64
-p: processes                      1982
-n: file descriptors               1024
-v: address space (kb)             unlimited
-w: locks                          unlimited
-e: scheduling priority            0
-r: real-time priority             0

分析core文件過程,如下圖所示。當(dāng)輸出log信息不完整時(shí),需要檢查下源碼和相關(guān)庫文件路徑是否設(shè)置好,可根據(jù)圖片中標(biāo)注處進(jìn)行設(shè)置。(展示的是測(cè)試代碼,非實(shí)際工程代碼)

圖片

實(shí)際代碼gdb分析log如下

/home/outapp/app # …/…/gdb xxx_capture core
GNU gdb (GDB) 7.6
Copyright ? 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “arm-hisiv300-linux”.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/…
Reading symbols from /home/outapp/app/xxx_capture…(no debugging symbols found)…done.
[New LWP 803]
[New LWP 789]
[New LWP 798]
[New LWP 807]
[New LWP 799]
[New LWP 791]
[New LWP 832]
[New LWP 797]
[New LWP 795]
[New LWP 802]
[New LWP 809]
[New LWP 790]
[New LWP 805]
[New LWP 804]
[New LWP 808]
[New LWP 796]
[New LWP 806]
[New LWP 810]
[New LWP 831]
[New LWP 833]
[Thread debugging using libthread_db enabled]
Using host libthread_db library “/lib/libthread_db.so.1”.
Core was generated by `xxx_capture capture 660’.
Program terminated with signal 11, Segmentation fault.
#0 0xb5e63b54 in memset () from /lib/libc.so.0
(gdb) bt
#0 0xb5e63b54 in memset () from /lib/libc.so.0
#1 0xb6e63064 in xxx3520D_Sample_OsdRegShowUpdata (ps8Contenx=0xb1dc2a70 " 000KM/H ", pstRegAttr=0x32f9e9c)
at SdkLogic/xxx3520dSample/xxx3520dOsd.c:436
#2 0xb6e63930 in xxx3520D_Sample_OsdShowGpsSpeed (pstRegAttr=0x32f9e9c, u8Speed=0000’) at SdkLogic/xxx3520dSample/xxx3520dOsd.c:621
#3 0xb6e4dc14 in xxxSdkAl_OsdShowGpsSpeed (pstRegAttr=0x32f9e9c, u8Speed=0000’) at SdkAppInt/xxxAHDSdkAL.c:474
#4 0xb6cb7b50 in OsdServiec::Osd_Reg_Show() () from /hi3520/lib/libxxxxxx_hi3520_AHDOsd.so
#5 0xb6cb726c in xxx_Osd_Display(void*) () from /hi3520/lib/libxxxxxx_hi3520_AHDOsd.so
#6 0xb6fc0f6c in start_thread () from /lib/libpthread.so.0
#7 0xb5e82134 in clone () from /lib/libc.so.0
#8 0xb5e82134 in clone () from /lib/libc.so.0
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)

小結(jié):以上定位內(nèi)存越界只是一個(gè)大體思路,實(shí)際情況多樣性,具體問題還需要具體分析,個(gè)人認(rèn)為如果只需要定位程序異常退出的話,用backtrace相關(guān)函數(shù)來代替gdb分析問題要輕量化很多。上述之所以使用gdb去分析問題是由于使用的交叉編譯是uclibc環(huán)境(uclibc環(huán)境下backtrace函數(shù)是沒實(shí)現(xiàn)的),就只能使用sdk提供的gdb工具了

二:為什么我電腦編譯出來的庫就暴露這個(gè)問題呢?

通過上面的方法已經(jīng)定位到是哪行代碼有bug,所以想再分析下我編譯出來的庫為啥就暴露這個(gè)問題了呢?分析得知是在生成.so庫時(shí)由于鏈接.o的順序不同導(dǎo)致庫里面全局變量數(shù)組的地址分布也有所不同。下面分析下log文件里具體不同點(diǎn),截圖貼上:

qiuhui@ubuntu:/mnt/hgfs/qh/work/app/SVN/?????$ arm-hisiv300-linux-objdump -t ???/lib?????.so > log

【圖一為我電腦編譯的】

圖片

【圖二為同事電腦編譯的】

圖片

由上圖可以觀察到兩個(gè)全局?jǐn)?shù)組變量“gs_s8Contenx”與“g_s32MaxFd”它們的地址有前后順序差異,圖一:“gs_s8Contenx地址0xfd9e4”小于“g_s32MaxFd地址0xfed34”,圖二:“gs_s8Contenx地址0xfdfd4”大于“g_s32MaxFd地址0xfdbd4”。正是由于這兩個(gè)地址的前后順序才導(dǎo)致我編的庫暴露了問題,因?yàn)槲揖幍膅s_s8Contenx地址小于g_s32MaxFd,代碼里剛好使用gs_s8Contenx數(shù)組時(shí)以超過數(shù)組元素最大值做賦值操作,從而引發(fā)大面積內(nèi)存越界,導(dǎo)致越界地址直接就踩到g_s32MaxFd變量地址了(踩到很多全局變量了),所以g_s32MaxFd數(shù)組的值被莫名修改,從而產(chǎn)生各種異常。當(dāng)然同事編譯的同樣也會(huì)使gs_s8Contenx越界,但由于gs_s8Contenx地址大于g_s32MaxFd,所以gs_s8Contenx剛好踩到的是一段不常用的地址,導(dǎo)致問題沒有及時(shí)暴露出來。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11213

    瀏覽量

    208735
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2980

    瀏覽量

    73824
  • 程序
    +關(guān)注

    關(guān)注

    116

    文章

    3766

    瀏覽量

    80763
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    632

    瀏覽量

    29116
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    深度分析Linux內(nèi)存使用方法

    一提到內(nèi)存管理,我們頭腦中閃出的兩個(gè)概念,就是虛擬內(nèi)存,與物理內(nèi)存。這兩個(gè)概念主要來自于linux內(nèi)核的支持。
    的頭像 發(fā)表于 08-20 09:00 ?7212次閱讀

    Linux kernel內(nèi)存管理模塊結(jié)構(gòu)分析

    基于上面章節(jié)的需求,Linux kernel從虛擬內(nèi)存(VM)、DMA mapping以及DMA buffer sharing三個(gè)角度,對(duì)內(nèi)存進(jìn)行管理.
    發(fā)表于 09-19 11:55 ?1731次閱讀
    <b class='flag-5'>Linux</b> kernel<b class='flag-5'>內(nèi)存</b>管理模塊結(jié)構(gòu)<b class='flag-5'>分析</b>

    Linux內(nèi)存管理是什么,Linux內(nèi)存管理詳解

    Linux內(nèi)存管理 Linux內(nèi)存管理是一個(gè)非常復(fù)雜的過程,主要分成兩個(gè)大的部分:內(nèi)核的內(nèi)存管理和進(jìn)程虛擬
    的頭像 發(fā)表于 05-11 17:54 ?5947次閱讀
    <b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b>管理是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>內(nèi)存</b>管理詳解

    想問下大家有沒有遇到過 時(shí)間消息沒又響應(yīng) 便執(zhí)行的 不是不是程序內(nèi)存管理越界了?

    想問下大家有沒有遇到過 時(shí)間消息沒又響應(yīng) 便執(zhí)行的 不是不是程序內(nèi)存管理越界了??
    發(fā)表于 05-25 16:29

    Linux上對(duì)進(jìn)程進(jìn)行內(nèi)存分析內(nèi)存泄漏定位

    Linux產(chǎn)品開發(fā)過程中,通常需要注意系統(tǒng)內(nèi)存使用量,和評(píng)估單一進(jìn)程的內(nèi)存使用情況,便于我們選取合適的機(jī)器配置,來部署我們的產(chǎn)品。Linux本身提供了一些工具方便我們達(dá)成這些需求,查
    發(fā)表于 07-09 08:15

    Linux ARM中斷向量重定位硬件平臺(tái)分析

    Linux ARM 中斷向量重定位分析
    發(fā)表于 07-19 12:34

    嵌入式linux內(nèi)存越界定位和解決的相關(guān)資料分享

    [url=https://blog.csdn.net/meejoy/article/details/41729585https://blog.csdn.net/killmice/article/details/38443343]https://blog.csdn.net/meejoy/article/details/41729585https://blog.csdn.net/killmice/article/details/38443343[/url]轉(zhuǎn)載于:https://www.cnblogs.com/erhu-67786482/p/10289256.html
    發(fā)表于 12-20 07:33

    Java程序內(nèi)存低效使用問題的分析

    Java程序內(nèi)存的低效使用是導(dǎo)致其性能問題的主要因素。該文分析了泄漏對(duì)象、蚍蜉對(duì)象和空閑對(duì)象3類導(dǎo)致內(nèi)存低效使用的情況,探討解決上述問題的方法,并提出構(gòu)造對(duì)象行為模式
    發(fā)表于 04-09 09:39 ?12次下載

    數(shù)組越界的故障模型及其檢測(cè)方法研究

    數(shù)組越界是C 程序中的常見故障,該類故障可能造成系統(tǒng)的崩潰。首先針對(duì)常見的數(shù)組越界故障進(jìn)行了分析,提出了檢測(cè)數(shù)組越界的判定準(zhǔn)則,建立了故障模
    發(fā)表于 09-24 10:49 ?16次下載

    一種改進(jìn)的虹膜邊界定位算法_汪良會(huì)

    一種改進(jìn)的虹膜邊界定位算法_汪良會(huì)
    發(fā)表于 03-14 17:38 ?5次下載

    基于SLUB的DEBUG功能,如何幫忙檢測(cè)內(nèi)存越界和訪問已經(jīng)釋放的內(nèi)存

    SLAB內(nèi)存分配器-SLUB的DEBUG功能,如何幫忙檢測(cè)內(nèi)存越界(out-of-bounds)和訪問已經(jīng)釋放的內(nèi)存(use-after-free)。
    的頭像 發(fā)表于 02-08 14:11 ?9539次閱讀
    基于SLUB的DEBUG功能,如何幫忙檢測(cè)<b class='flag-5'>內(nèi)存</b><b class='flag-5'>越界</b>和訪問已經(jīng)釋放的<b class='flag-5'>內(nèi)存</b>

    分析Linux操作系統(tǒng)的內(nèi)存

    前言:在Linux上不像在Windows上看內(nèi)存那樣方便,而且還有Swap這個(gè)新的概念,所以知道如何來看Linux內(nèi)存還是有一定意義的
    的頭像 發(fā)表于 03-31 16:43 ?1338次閱讀

    Linux內(nèi)核源碼分析-進(jìn)程的哪些內(nèi)存類型容易引起內(nèi)存泄漏?

    Linux內(nèi)核主要學(xué)習(xí)內(nèi)容可以分為三大塊:進(jìn)程、內(nèi)存及協(xié)議棧。今天就說說內(nèi)存泄露的問題。相信你在平時(shí)的工作中,應(yīng)該遇到過下面這些場景: 伴隨著服務(wù)器中的后臺(tái)任務(wù)持續(xù)地運(yùn)行,系統(tǒng)中可用內(nèi)存
    發(fā)表于 01-14 13:02 ?6次下載
    <b class='flag-5'>Linux</b>內(nèi)核源碼<b class='flag-5'>分析</b>-進(jìn)程的哪些<b class='flag-5'>內(nèi)存</b>類型容易引起<b class='flag-5'>內(nèi)存</b>泄漏?

    Linux 內(nèi)存管理總結(jié)

    一、Linux內(nèi)存管理概述 Linux內(nèi)存管理是指對(duì)系統(tǒng)內(nèi)存的分配、釋放、映射、管理、交換、壓縮等一系列操作的管理。在
    的頭像 發(fā)表于 11-10 14:58 ?487次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)存</b>管理總結(jié)

    jvm內(nèi)存溢出該如何定位解決

    超出限制和堆空間不足。 定位JVM內(nèi)存溢出問題是一個(gè)比較復(fù)雜的任務(wù),需要結(jié)合工具和技術(shù)來進(jìn)行分析和解決。本文將介紹一些常用的調(diào)試和解決內(nèi)存溢出問題的工具和技術(shù)。 一、理解JVM
    的頭像 發(fā)表于 12-05 11:05 ?1275次閱讀