Skip to content

Instantly share code, notes, and snippets.

@holishing
Created January 14, 2019 17:59
Show Gist options
  • Save holishing/a4ee1582947d0adea7e29c8ba01c6ecc to your computer and use it in GitHub Desktop.
Save holishing/a4ee1582947d0adea7e29c8ba01c6ecc to your computer and use it in GitHub Desktop.
Maple3 BRH crash
作者 lantw44 ([#################]) 看板 lantw44
標題 Re: [測試] 轉得過去嗎?
時間 2018/02/12 Mon 23:30:38
> 作者: lantw44 ([#################]) 看板: lantw44
> 標題: Re: [測試] 轉得過去嗎?
> 時間: 2018/02/11 Sun 17:26:35
>
> 於是我先把 SIGBUS 和 SIGSEGV 的 signal handler 拿掉了,總覺得把這種 signal 抓
> 下來實在很沒意義,只是造成 debug 困難而已,不知道當初為什麼會這樣寫。
於是看來 signal handler 存在的目的是呼叫 u_exit 讓已經當掉的使用者不會繼續出現
在使用者名單上。不過我想這功能實際上只對站務帳號有用,因為普通使用者登入的時候
會提示是否踢掉重覆登入,我想大部分的人應該都會選「是」吧。
儘管如此我依然不認為一個普通程式可以對 SIGBUS 和 SIGSEGV 這種代表程式錯誤的訊
號設定 signal handler。程式本身都已經發生嚴重錯誤了,還能叫 u_exit?
> r2 推:有印象又叫回 abort_bbs 這個部分@@ 超奇怪的@@ 0211 20:39
> lantw44 推:再來登入一下收集 core dump 吧…… 0211 21:19
> r2 推:ok @@ 0211 23:32
> lantw44 推:我把 setrlimit(RLIMIT_DATA, &limit) 這行註解掉了 0212 00:54
> lantw44 說:再來試試看吧,總覺得這類資源限制還蠻容易造成問題的 0212 00:56
> r2 推:有再測試了, 進去 (B)oards (F)avorite 或 s 過去 lantw44 板 0212 02:14
> r2 說:還是都會斷線@@ 0212 02:15
一開始會猜 setrlimit 是因為 dmesg 曾經有出現過類似訊息,但既然還是會斷線,那就
代表不是這個的問題了。BBS 本身記憶體用量就不高,而 glibc 的 malloc 實作也只有
在配置小量空間時才會用 sbrk,大量空間用 mmap 應該是不受 RLIMIT_DATA 限制。
------------------------------------------------------------------------------
前篇訊息的回覆就到這裡,先說結論:
你的帳號的 .BRH 檔案爛掉了
以下是一些 debug 的過程和細節,有興趣就看看吧,至於 .BRH 如何處理結尾再說。
------------------------------------------------------------------------------
$ gdb ~bbs/bin/bbsd 'core.!home!bbs!bin!bbsd.signal-11.pid-26147'
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
......
Reading symbols from /home/bbs/bin/bbsd...done.
[New LWP 26147]
Core was generated by `bbsd -i'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xf7582f06 in _IO_vfscanf_internal (s=0xff6f5470, format=0xf7692108
"%hu%n:%hu%n:%hu%n", argptr=0xff6f5548
"vUo\377|Uo\377xUo\377|Uo\377zUo\377|Uo\377", errp=0x0)
at vfscanf.c:279
首先這個什麼 scanf 的我想完全不重要,stack overflow 以後整個程式就不知道在跑
什麼東西了,之後看 backtrace 我也是直接忽略這段。
(gdb) set height unlimited
(gdb) bt
......
#56604 0x565abcbe in abort_bbs () at bbsd.c:178
#56605 0x565b1215 in brh_alloc (tail=0xc1fb3af8, size=1887295246) at
board.c:97
#56606 0x565b208f in brh_save () at board.c:668
#56607 0x565abb2f in u_exit (mode=0x565e2e50 "AXXED") at bbsd.c:121
#56608 0x565abcbe in abort_bbs () at bbsd.c:178
#56609 0x565b1215 in brh_alloc (tail=0xc1fb3af8, size=1887295246) at
board.c:97
#56610 0x565b208f in brh_save () at board.c:668
#56611 0x565abb2f in u_exit (mode=0x565e2e50 "AXXED") at bbsd.c:121
#56612 0x565abcbe in abort_bbs () at bbsd.c:178
#56613 0x565b1215 in brh_alloc (tail=0xc1fb3af8, size=1887295246) at
board.c:97
#56614 0x565b208f in brh_save () at board.c:668
#56615 0x565abb2f in u_exit (mode=0x565e5da2 "EXIT ") at bbsd.c:121
#56616 0x565c384a in goodbye () at menu.c:213
#56617 0x565c4359 in menu () at menu.c:1137
#56618 0x565adc5d in tn_main () at bbsd.c:1201
#56619 0x565ae600 in main (argc=2, argv=0xffef3194) at bbsd.c:1657
總之可以看到第一次 u_exit 是使用者自己從選單按離站而呼叫的,之後的每一次都是
由 brh_alloc 透過 abort_bbs 叫的。這裡並沒有 signal handler 介入,所以前一篇
文章所說一直重複收到 signal 而進入 abort_bbs 的假設應該是錯誤的,應該是每次只
要走到 brh_alloc 它就會呼叫 abort_bbs,而 abort_bbs 又會叫回 brh_alloc,如此
重複遞迴呼叫直到 stack overflow 收 SIGSEGV 被系統砍掉。
於是我們先來看看 brh_alloc 為什麼會去叫 abort_bbs。
(gdb) up 56613
#56613 0x565b1215 in brh_alloc (tail=0xc1fb3af8, size=1887295246) at
board.c:97
97 abort_bbs();
這附近的程式長得像是這樣:
base = (int *) realloc((char *) base, size);
if (base == NULL)
abort_bbs();
很明顯是 realloc 失敗了才會叫到 abort_bbs,但是為什麼會失敗?
(gdb) p size
$1 = 1887295246
所以說大小出錯了,那會是錯在什麼地方?
base = brh_base;
n = (char *) tail - (char *) base;
size += n;
其中 size 是參數,由 brh_save 傳入,而 brh_save 傳入的 size 是個常數,因此
問題應該是出在後面的 n。
(gdb) p n
$2 = 1776274024
(gdb) p tail
$3 = (int *) 0xc1fb3af8
base 因為已經被 realloc 的回傳值蓋掉了,所以我們從全域的 brh_base 來看。
(gdb) p brh_base
$4 = (int *) 0x581b7090
(gdb) p brh_tail
$5 = (int *) 0x581b71d4
由此可見 brh_base 和 brh_tail 的值都正常,是 brh_save 傳入的 tail 不知道為什麼
大的這麼離譜,才造成 n 的值大到 realloc 不可能成功。
回到上層的 brh_save 看看出了什麼事。
(gdb) up
#56614 0x565b208f in brh_save () at board.c:668
668 tail = brh_alloc(base, sizeof(time_t) * MAXBOARD);
這附近的程式長得像是這樣:
/* Thor.980830: lkchu patch: 還沒 load 就不用 save */
if (!(base = brh_base))
return;
brh_put();
/* save history of un-zapped boards */
bits = brd_bits;
head = base;
tail = brh_tail;
while (head < tail)
{
bhno = bstamp2bno(*head);
size = head[2] * sizeof(time_t) + sizeof(BRH);
if (bhno >= 0 && !(bits[bhno] & BRD_Z_BIT))
{
if (base != head)
memcpy(base, head, size);
base = (int *) ((char *) base + size);
}
head = (int *) ((char *) head + size);
}
/* save zap record */
tail = brh_alloc(base, sizeof(time_t) * MAXBOARD);
brh_alloc 中爛掉的 tail 值來自 brh_save 中爛掉的 base 值。而 base 的初始值來自
brh_base 全域變數。brh_base 變數的值看起來並沒有爛掉,所以一定是這個迴圈的某個
地方把它改爛了。而會動到 base 的只有一行:
base = (int *) ((char *) base + size);
由此可推知 size 也爛掉了,而動到 size 的也只有一行:
size = head[2] * sizeof(time_t) + sizeof(BRH);
head 指向從檔案裡面讀出來的內容,所以說就是 .BRH 檔案爛掉了。
(gdb) p base
$6 = (int *) 0xc1fb3af8
(gdb) p size
$7 = 1776273804
brh_save 的 base 確實和 brh_alloc 的 tail 相符,而這個 size 很明顯有問題。
所以說我就把 ~bbs/usr/r/r2/.BRH 打開來看了。
------------------------------------------------------------------------------
以下是 ~bbs/usr/r/r2/.BRH 的檔案內容。為了避免洩漏隱私資料,不重要的部份這裡都
用 xx 來代替。
00000000 - xx xx xx 59 - xx xx xx 5a - 00 00 00 00 - xx xx xx 3b
00000010 - xx xx xx 5a - 01 00 00 00 - xx xx xx d9 - xx xx xx 3b
00000020 - xx xx xx 5a - 00 00 00 00 - xx xx xx 35 - xx xx xx 5a
00000030 - 00 00 00 00 - xx xx xx 3b - xx xx xx 5a - 20 00 00 00 應有 32 個
00000040 - xx xx xx 5a - xx xx xx 5a - xx xx xx 5a - xx xx xx 59 2 個區間
00000050 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 6 個區間
00000060 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 10 個區間
00000070 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 14 個區間
00000080 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 18 個區間
00000090 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 22 個區間
000000a0 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 26 個區間
000000b0 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 30 個區間
000000c0 - xx xx xx 3b - xx xx xx 5a - 01 00 00 00 - xx xx xx da
000000d0 - xx xx xx 3b - xx xx xx 5a - 00 00 00 00 - xx xx xx 3b
000000e0 - xx xx xx 5a - 00 00 00 00 - xx xx xx da - xx xx xx 59
000000f0 - xx xx xx 5a - 14 00 00 00 - 60 f2 77 5a - xx xx xx 5a 應有 20 個
00000100 - xx xx xx 5a - xx xx xx 5a - xx xx xx 5a - xx xx xx 5a 3 個區間
00000110 - xx xx xx da - xx xx xx 5a - xx xx xx 5a - xx xx xx da 6 個區間
00000120 - xx xx xx da - xx xx xx da - xx xx xx d9 - xx xx xx d9 10 個區間
00000130 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 - xx xx xx d9 14 個區間
00000140 - xx xx xx d9 - xx xx xx d9 16 個區間
檔案格式可以參考之前的文章。所有資料都對齊 4 bytes 的邊界,淺藍色代表一筆看板
資料的開頭,綠色代表正確的長度欄位,紅色代表錯誤的長度欄位。
其實這個檔案還算容易看,因為只有長度欄位的上面幾個 byte 會是 0,其他欄位因為
都是代表時間而不可能是 0,由此可以很容易地找出每筆看板資料的位置。
上面已經標示,第一個紅字的地方缺了 2 個區間,第二個紅字的地方缺了 4 個區間。
那前面的 1776273804 是怎麼來的呢?
size = head[2] * sizeof(time_t) + sizeof(BRH);
^^^^^^^^^^^^^^ ^^^^^^^^^^^
4 12
而如果我們把標示成黃色的地方的數值代入 head[2]:
0x5a77f260 * 4 + 12 = 6071241100 = 0x169dfc98c
捨棄超過 32-bit 的部份可以得到:
0x69dfc98c = 1776273804
------------------------------------------------------------------------------
於是我想就兩種解法:
1. 把那兩個紅字的地方改回正確數值。
2. 直接把 .BRH 砍掉。
我個人偏好第一種,你有什麼想法呢?
雖然說我也不知道為什麼 .BRH 檔案會爛掉……
--
┼─╮ ╭───────╮
│ │台南一中.索尼小站∣sony.TFcis.org│ │
╰─────────╯ ╰─┼
by lantw44 from 172.20.7.2 (tinc: lantw44)
→ r2 推:同意第一種… 覺得神秘@@ 0213 18:08
→ lantw44 推:我改好了,再試試看吧 0214 14:41
→ r2 推:發現夢大也會發生這個問題...@@ 0215 12:23
→ lantw44 推:你說 .BRH 不知道為什麼自己壞掉嗎? 0215 14:56
→ r2 推:對...@@ 0216 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment