본문 바로가기
IT

GDB로 파이썬 buffer overflow detected(segmentation fault) 디버깅 하는 방법

by eddy's warehouse 2024. 2. 13.

파이썬 코드를 작성 중에 buffer overflow로 core dump가 발생합니다. 파이썬에서 발생하는 문제 같지는 않고, 사용중인 ODBC 라이브러리에서 문제가 발생하는 것으로 보입니다. 오늘은 파이썬에서 C/C++ 라이브러리를 디버깅 하는 법을 알아보겠습니다.

GDB로 파이썬 buffer overflow detected(segmentation fault) 디버깅 하는 방법 썸네일

라이브러리 에러 발생

unixodbc를 이용해서 odbc library를 통해서 db에 접근하는 간단한 프로그램을 테스트 중입니다. 그런데, db library에서 아래와같은 에러로 죽어버립니다. unixodbc의 trace로깅을 켜 놓았는데도 문제를 찾을 수가 없습니다.

 

*** buffer overflow detected ***: terminated
Aborted (core dumped)

위와 같은 에러 발생 후 core dump된 파일을 찾아보니 /var/crash/_usr_bin_python3.10.1000.crash 파일이 dump 되어있습니다. 하지만 해당 파일은 바이너리 core 파일이 아니고 text 파일로 남겨진 정보일 뿐입니다.

% file /var/crash/_usr_bin_python3.10.1000.crash
/var/crash/_usr_bin_python3.10.1000.crash: ASCII text, with very long lines (49500)

즉, 위 파일로는 디버깅을 진행할 수가 없습니다.

내용은 한번 열어보았는데, 이걸 본다고 어디서 문제가 발생했는지를 알 수가 없습니다.

 

gdb를 이용한 파이썬 디버깅

파이썬 디버깅을 하기 위해서는 pip를 통해서 libpython을 설치합니다.

% pip install libpython
Defaulting to user installation because normal site-packages is not writeable
Collecting libpython
  Downloading libpython-0.2.tar.gz (15 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from libpython) (2.25.1)
Building wheels for collected packages: libpython
  Building wheel for libpython (setup.py) ... done
  Created wheel for libpython: filename=libpython-0.2-py3-none-any.whl size=14386 sha256=14f3841149a489d217ee1adba4cb7e28ba804103c824a64b595344fd9c0557ac
  Stored in directory: /home/altibase/.cache/pip/wheels/46/0a/1b/26af5c6af34e0f5d7eeb6dd878b18109cfbb1f2b1dc531a35b
Successfully built libpython
Installing collected packages: libpython
Successfully installed libpython-0.2

 

$HOME의 .config 디렉토리로 이동하여 gdb 디렉토리를 생성합니다.

% pwd
/home/lswhh/.config
lswhh: ~/.config
% mkdir -p gdb

 

libpython.py 파일을 다운 받아야 하는데, 자신이 설치한 python과 같은 버전의 libpython.py 파일을 사용해야합니다.

libpython.py는 파이썬의 소스 코드에 포함되어 있는 gdb 확장입니다. 이 파일은 파이썬 자체와 파이썬/C 확장을 디버깅하는 데 유용합니다1libpython.py 파일을 다운로드하려면, 파이썬의 소스 코드를 다운로드해야 합니다.

파이썬의 소스 코드는 파이썬의 공식 웹사이트에서 다운로드할 수 있습니다. 소스 코드를 다운로드한 후, Tools/gdb/libpython.py 경로에서 libpython.py 파일을 찾을 수 있습니다.

파이썬 공식 소스 다운은 아래 링크에서 받으실 수 있습니다.

https://www.python.org/downloads/source/
저의 경우 3.10.12를 사용 중이므로 해당 버전 확인후 다운로드를 받았습니다.

% python3 --version
Python 3.10.12

% wget https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz --no-check-certificate
--2024-02-13 16:25:15--  https://www.python.org/ftp/python/3.10.12/Python-3.10.12.tgz 
http://www.python.org (http://www.python.org) 해석 중... 146.75.48.223, 2a04:4e42:7c::223
다음으로 연결 중: http://www.python.org (http://www.python.org)|146.75.48.223|:443... 연결했습니다.
경고: ‘CN=GlobalSign Atlas R3 DV TLS CA 2023 Q2,O=GlobalSign nv-sa,C=BE’에서 발급한 http://www.python.org 인증서를 검증할 수 없습니다:
  발급자의 신뢰도를 로컬에서 검증할 수 없습니다.
HTTP 요청을 보냈습니다. 응답 기다리는 중... 200 OK
길이: 26103005 (25M) [application/octet-stream]
저장 위치: ‘Python-3.10.12.tgz’

Python-3.10.12.tgz                                  100%[================================================================================================================>]  24.89M  6.86MB/s    / 3.8s

2024-02-13 16:25:19 (6.53 MB/s) - ‘Python-3.10.12.tgz’ 저장함 [26103005/26103005]


그 후 아래 명령을 통해서 소스 압축을 풀고 libpython.py를 아까 만든 gdb 디렉토리로 이동해 줍니다.

tar xvzf Python-3.10.12.tgz
% cd Python-3.10.12/Tools/gdb
% cp libpython.py ~/.config/gdb/

 

$ vi ~/.gdbinit

아래와 같은 python 코드를 .gdbinit으로 작성합니다. 제일 첫 줄의 python은 gdb에게 파이썬 코드라는 것을 알려줍니다.

python
import gdb
import sys
import os
sys.path.insert(0, os.path.expanduser("~/.config/gdb"))

def setup_python(event):
	import libpython
gdb.events.new_objfile.connect(setup_python)
end

 

 

이후 다음의 명령을 통해서 디버깅을 할 수 있습니다.

 

% gdb --args python3 test.py
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 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 "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from python3...
(No debugging symbols found in python3)
(gdb) run
Starting program: /usr/bin/python3 test.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
*** buffer overflow detected ***: terminated

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737352492864) at ./nptl/pthread_kill.c:44
44      ./nptl/pthread_kill.c: 그런 파일이나 디렉터리가 없습니다.
(gdb) bt
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737352492864) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737352492864) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737352492864, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7c42476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c287f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7c89676 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7ddb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:155
#6  0x00007ffff7d3659a in __GI___fortify_fail (msg=msg@entry=0x7ffff7ddb8d4 "buffer overflow detected") at ./debug/fortify_fail.c:26
#7  0x00007ffff7d34f16 in __GI___chk_fail () at ./debug/chk_fail.c:28
#8  0x00007ffff7c818df in _IO_str_chk_overflow (fp=<optimized out>, c=<optimized out>) at ./libio/iovsprintf.c:35
#9  0x00007ffff7c8de34 in __GI__IO_default_xsputn (n=<optimized out>, data=<optimized out>, f=<optimized out>) at ./libio/genops.c:399
#10 __GI__IO_default_xsputn (f=0x7fffffffcb80, data=<optimized out>, n=6) at ./libio/genops.c:370
#11 0x00007ffff7c75fca in outstring_func (done=28, length=6, string=0x7ffff73e6b64 " bits)", s=0x7fffffffcb80) at ../libio/libioP.h:947
#12 __vfprintf_internal (s=s@entry=0x7fffffffcb80, format=format@entry=0x7ffff73e6b57 "%p -> %ld (%d bits)", ap=ap@entry=0x7fffffffccc0, mode_flags=mode_flags@entry=6)
    at ./stdio-common/vfprintf-internal.c:1593
#13 0x00007ffff7c81989 in __vsprintf_internal (string=0x7fffffffce10 "0x7fffffffd17c -> 331797 (32 bi", maxlen=<optimized out>, format=0x7ffff73e6b57 "%p -> %ld (%d bits)",
    args=args@entry=0x7fffffffccc0, mode_flags=6) at ./libio/iovsprintf.c:95
#14 0x00007ffff7d34a31 in ___sprintf_chk (s=<optimized out>, flag=<optimized out>, slen=<optimized out>, format=<optimized out>) at ./debug/sprintf_chk.c:40
#15 0x00007ffff73cdc0b in SQLGetDiagRecW () from /lib/x86_64-linux-gnu/libodbc.so.2
....

 

당연한 결과지만 디버깅으로 컴파일 되지 않고 코드가 없는 것들은 디버깅을 할 수는 없고 함수 정도만 볼 수 있습니다.

이상으로 gdb로 파이썬 디버깅 하는 방법을 알아보았습니다.

facebook twitter kakaoTalk kakaostory naver band shareLink