Gaim 中 Jabber 的 bug

首先是在 Windows 上遇到的,有时候上线的时候就崩溃了,然后重新启动仍然还是会崩溃,这样几次才能起来。
由于在 Windows 上不好调试,不知道到底是哪里的问题。后来在 Linux 也碰到,会产生 core。gdb 后,
发现是 jabber 的问题,backtrace 如下:

  1. (gdb) bt
  2. #0  0xb75ba7c7 in raise () from /lib/tls/libc.so.6
  3. #1  0xb75bc06b in abort () from /lib/tls/libc.so.6
  4. #2  0x080f6a5b in sighandler (sig=11) at gtkmain.c:175
  5. #3  <signal handler called>
  6. #4  0xb7717c56 in xmlCreateEntityParserCtxt () from /usr/lib/libxml2.so.2
  7. #5  0xb77294a3 in xmlParseChunk () from /usr/lib/libxml2.so.2
  8. #6  0xb6e90aef in jabber_parser_process (js=0x84c4708, buf=0x1 <Address 0x1 out of bounds>, len=1701999730) at parser.c:287
  9. #7  0xb6e88cef in jabber_recv_cb_ssl (data=0x84c4908, gsc=0x84eb7f8, cond=GAIM_INPUT_READ) at jabber.c:356
  10. #8  0x080e2393 in gaim_gtk_io_invoke (source=0x84c84d0, condition=G_IO_IN, data=0x84de8f8) at gtkeventloop.c:74
  11. #9  0xb78587ef in g_io_channel_unix_get_fd () from /usr/lib/libglib-2.0.so.0
  12. #10 0xb782fe2c in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
  13. #11 0xb7833176 in g_main_context_check () from /usr/lib/libglib-2.0.so.0
  14. #12 0xb7833537 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
  15. #13 0xb7ca64e1 in gtk_main () from /usr/lib/libgtk-x11-2.0.so.0
  16. #14 0x080f68f7 in main (argc=Cannot access memory at address 0x65727472) at gtkmain.c:769

而且 gaim -d 出来的信息也非常固定,相关的如下:

  1. jabber: Sending:
  2.   <?xml version='1.0' ?>
  3. jabber: Sending:
  4.   <stream:stream to='gmail.com' xmlns='jabber:client'
  5.                  xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>
  6. jabber: Recv (177):
  7. 1)  <?xml version="1.0" encoding="UTF-8"?>
  8.     <stream:stream from="gmail.com" id="X1E24799AB597443A" version="1.0"
  9.                    xmlns:stream="http://etherx.jabber.org/streams"
  10.                    xmlns="jabber:client">
  11. jabber: Recv (189):
  12. 2)  <stream:features>
  13.       <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
  14.       <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
  15.         <mechanism>X-GOOGLE-TOKEN</mechanism>
  16.       </mechanisms>
  17.     </stream:features>
  18. jabber: Sending:
  19.   <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
  20. jabber: Recv (50):
  21. 3)  <proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
  22. ......
  23. jabber: Sending (ssl):
  24.   <stream:stream to='gmail.com' xmlns='jabber:client'
  25.                  xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>
  26. jabber: Recv (ssl)(177):
  27. 4)  <?xml version="1.0" encoding="UTF-8"?>
  28.     <stream:stream from="gmail.com" id="XF341BDEC9BA25263" version="1.0"
  29.                    xmlns:stream="http://etherx.jabber.org/streams"
  30.                    xmlns="jabber:client">
  31. jabber: Recv (ssl)(166):
  32. 5)  <stream:features>
  33.       <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
  34.         <mechanism>PLAIN</mechanism>
  35.         <mechanism>X-GOOGLE-TOKEN</mechanism>
  36.       </mechanisms>
  37.     </stream:features>
  38. sasl: Mechs found: PLAIN X-GOOGLE-TOKEN
  39. jabber: Sending (ssl):
  40.   <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
  41.         mechanism='PLAIN'>XXXXXXXX</auth>
  42. jabber: Recv (ssl)(51):
  43. 6)  <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
  44. jabber: Sending (ssl):
  45.   <stream:stream to='gmail.com' xmlns='jabber:client'
  46.                  xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>

在对照了 [XMPP Core] 以后,协议是没错的,
但错误总是发生在第二次 Jabber 协议初始化并且发送了一次客户端消息之后。相关的代码如下:

  1. 01 jabber.c:510:jabber_login() 开始登录
  2. 02 jabber.c:472:jabber_login_connect()
  3. 03 jabber.c:421:jabber_login_callback()
  4.     jabber.c:982:jabber_stream_set_state() 参数是 JABBER_STREAM_INITIALIZING
  5.       jabber.c:57:jabber_stream_init() XML 解析器清零,客户端发起协议,
  6. 04 jabber.c:366:jabber_recv_cb() 接收到数据后,回调函数被调用,进行处理
  7. 05 parser.c:271:jabber_parser_process() (391 行) 如果是第一次,则初始化 XML 解析器,
  8.     其解析的处理函数在 parser.c:207:jabber_parser_libxml 定义中。然后对数据进行 XML 解析。
  9. 06 parser.c:171:jabber_parser_element_end_libxml() 在一个元素结束的时候进行一些操作,
  10.     通常是一次数据全部解析完以后才真正开始解析 Jabber 协议数据。
  11. 07 jabber.c:167:jabber_process_packet() 针对包中不同元素的名字可能会采取不同的操作。
  12.  
  13. 从上面提供的调试数据(主要针对 Recv)可以看到,处理了 stream:features 和 starttls,然后返回。
  14.  
  15. 然后在收到 proceed 包的时候:
  16. 08 jabber.c:464:tls_init()
  17. 09 jabber.c:400:jabber_login_callback_ssl() SSL 登录的回调函数,并增加输入的回调函数
  18. 10 jabber.c:339:jabber_recv_cb_ssl() 接下来的处理就和第 5-7 步一样了。
  19. 以后的调试信息就有了(ssl)的标记
  20.  
  21. 接下来就收到了第 5 个包,开始处理:
  22. 11 jabber.c:125:jabber_stream_features_parse()
  23. 12 auth.c:302:jabber_auth_start()
  24. 13 auth.c:62:finish_plaintext_authentication()  发送了 auth 这个包。
  25.  
  26. 接着收到了第 6 个包,是 success:
  27. 14 auth.c:736:jabber_auth_handle_success()
  28. 15 jabber.c:982:jabber_stream_set_state() 参数是 JABBER_STREAM_REINITIALIZING
  29. 16 jabber.c:57:jabber_stream_init() XML 解析器清零,客户端重新发起协议,

这里发送完以后就发生了 core dump,应该还没有收到服务器端的响应,不然会有调试信息打出来的。
所以就有了矛盾,不知道为什么会出错。我感觉可能是 XML 解析器的状态问题。

Gaim-dev 上有人提交了 patch

原因是在 reinit 的时候,还是在 xml 的回调函数中运行的,而它把 xml context 给释放了,所以会发生错误。
我上面的猜想是对的,不过没有再仔细想想。SVN Trunk 上应该已经解决。

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • You can use BBCode tags in the text.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
  __  __          ___     ____                ___                
| \/ | ___ ( _ ) / ___| _ __ ___ ( _ ) __ __ __ __
| |\/| | / __| / _ \ | | | '_ ` _ \ / _ \ \ \/ / \ \/ /
| | | | \__ \ | (_) | | |___ | | | | | | | (_) | > < > <
|_| |_| |___/ \___/ \____| |_| |_| |_| \___/ /_/\_\ /_/\_\
Enter the code depicted in ASCII art style.