前言
使用tgt-1.0.75创建好target之后,在initiator端执行login操作大约卡3s~5s左右。同时观察tgt,CPU消耗到达100%。
一度怀疑是glibc的版本问题,在多个发行版上测试,有的发行版上会coredump。
分析
backtrace
用gdb分析coredump文件,拿到backtrace:
代码语言:javascript复制Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f58a410ccc0 in _IO_vfprintf_internal (s=s@entry=0x7ffdec98fdc0, format=<optimized out>,
format@entry=0x432bc8 "d Mode page %d (0xx), index: %dn", ap=ap@entry=0x7ffdec98ff38) at vfprintf.c:1632
#1 0x00007f58a41d4896 in ___vsnprintf_chk (s=0x7ffdec990010 "", maxlen=<optimized out>, flags=1,
slen=<optimized out>, format=0x432bc8 "d Mode page %d (0xx), index: %dn", args=args@entry=0x7ffdec98ff38)
at vsnprintf_chk.c:63
#2 0x00007f58a41d47f8 in ___snprintf_chk (s=<optimized out>, maxlen=<optimized out>, flags=<optimized out>,
slen=<optimized out>, format=<optimized out>) at snprintf_chk.c:34
#3 0x000000000041f6e7 in scsi_sprintf (str=<optimized out>, size=<optimized out>, format=0x432a88 "%-*s",
format=0x432a88 "%-*s") at util.h:255
#4 0x000000000042043a in spc_inquiry (host_no=<optimized out>, cmd=0x123ff40) at spc.c:343
#5 0x000000000041935a in target_cmd_perform (tid=<optimized out>, cmd=0x123ff40) at target.c:1168
#6 0x000000000040a66b in iscsi_target_cmd_queue (task=0x123fe70) at iscsi/iscsid.c:1387
#7 iscsi_scsi_cmd_execute (task=task@entry=0x123fe70) at iscsi/iscsid.c:1407
#8 0x000000000040a93e in iscsi_task_execute (task=task@entry=0x123fe70) at iscsi/iscsid.c:1527
#9 0x000000000040b817 in iscsi_task_queue (task=0x123fe70) at iscsi/iscsid.c:1614
#10 iscsi_task_rx_done (conn=0x123b360) at iscsi/iscsid.c:1744
#11 iscsi_rx_handler (conn=conn@entry=0x123b360) at iscsi/iscsid.c:2222
#12 0x0000000000411f48 in iscsi_tcp_event_handler (fd=<optimized out>, events=1, data=0x123b360)
at iscsi/iscsi_tcp.c:278
#13 0x00000000004152df in event_loop () at tgtd.c:432
#14 0x0000000000407191 in main (argc=<optimized out>, argv=<optimized out>) at tgtd.c:624
backtrace正常,相关的参数也是正常的,和预期的现象不符合。
gcc & glibc的版本问题
一共测试了三个版本,分别是:
gcc-5.4/glibc-2.23 --> coredump
代码语言:javascript复制gcc-6.3/libc-2.24 --> CPU 100%
gcc-7.3/libc-2.27 --> coredump
不同的版本的gcc和glibc的情况,并非完全一致。
tgt的版本对比
考虑到tgt是一个比较稳定的软件,尝试回退到上一个版本1.0.74,发现login操作瞬间完成。
在两个版本之间,除了README更新,只有一个代码更新的commit : https://github.com/fujita/tgt/commit/2de8bebe132e3b998bf4848d0bd22b50367ad4b8
发现这里有改动导致的问题。
va_start嵌套的问题
va_start经常被用来处理可变参数的情况,经过测试发现,在处理"%-*s"这个特定的格式情况下,如果父函数先调用va_start处理,子函数再调用va_start处理,就会出现上述的bug。因为sprintf函数本身也是使用了va_start,而且tgt封装的函数也使用了va_start,导致了这个问题。
采用了walk around的方式来修复这个问题,避免va_start的嵌套即可。
给maintainer发送了patch,maintainer接受并push到了upstream。
https://github.com/fujita/tgt/commit/0bf3f6915282dc8df2f02e6da30814951e52d179
最新的upstream和下一个版本,将会修复这个问题。