Opened 9 years ago

Closed 9 years ago

#4264 closed defect (fixed)

ffmpeg supposedly hangs if RTSP session id contains $ or +

Reported by: Sebastian Rasmussen Owned by:
Priority: important Component: avformat
Version: git-master Keywords: rtsp deadlock
Cc: christian@cmolabs.org, marko.lukat@kyi.biglobe.ne.jp Blocked By:
Blocking: Reproduced by developer: no
Analyzed by developer: no

Description

Summary of the bug:

GStreamer got a bug report a while back where christian mentioned that ffmpeg hangs when RTSP session ids in gst-rtsp-server contain $ or +

Now, RFC 2326 actually does not state that characters can or should be URI-escaped in a session id, only that linear whitespace should be. However whitespace is not among the allowed characters in a session id, and neither is % which would be the result of URI-escaping.

Therefore GStreamer recently accepted a patch to refrain from URI-escaping the session id in the RTSP session header. Instead the argument is that ffmpeg should not hang if it encounters $ or + in the header. While I have not reproduced this myself I'm CCing christian who reported it. Finally I did quickly look at the RTSP header parsing code in ffmpeg and I fail to find any code that actually cares about the session id's constituing characters.

Perhaps the problem with $ is that the session id is mixed up with the $ used to separate multiple channels multiplexed over the same RTSP TCP connection? I'm far from certain about this, but at least it is part of the parsing and uses the same character as delimiter.

christian if you can still reproduce this, please chime in here and help out in getting a better fix for this problem.

Change History (7)

comment:1 by kuroneko, 9 years ago

FWIW, today I also came across this issue. I use ffmpeg (2.7.2) to extract the stream from a GStreamer equipped IP camera.

The server response for the session ID is like this:

Session: .5U+3chv$$2uwBK.; timeout=60

As soon as the first $ is encountered it (ffmpeg) attempts to skip a number of bytes based on the following data. What puzzles me is that the RFC mentions the "safe" character range as follows:

safe = "\$" | "-" | "_" | "." | "+"

Note the backslash in front of the $. Whatever I can do to help, let me know.

Last edited 9 years ago by kuroneko (previous) (diff)

in reply to:  1 ; comment:2 by Carl Eugen Hoyos, 9 years ago

Keywords: rtsp deadlock added
Priority: normalimportant

Replying to kuroneko:

FWIW, today I also came across this issue.

Then please test current FFmpeg git head and provide a backtrace when FFmpeg hangs.

comment:3 by Sebastian Rasmussen, 9 years ago

I used gst-rtsp-server to reproduce this locally at my machine just now. I made gst-rtsp-server unconditionally _always_ choose to emit Session-headers starting with two dollar signs.

The Session header used when I got the backtrace was "$$8fytI8KQRmjSJ_". I can see part of that in buf in frame 4 below. Should I be able to do that? Now that I can reproduce this consistently I'm able to help out in debugging this.

#0  0x00007ffff586153d in poll () 
#1  0x00000000005ccf7a in ff_network_wait_fd (write=0, fd=6)
    at libavformat/network.c:78
#2  ff_network_wait_fd_timeout (fd=6, write=write@entry=0, timeout=0, 
    int_cb=int_cb@entry=0x7fffe4005810)
    at libavformat/network.c:90
#3  0x0000000000620c55 in tcp_read (h=h@entry=0x7fffe40057e0, 
    buf=buf@entry=0x7fffea867c45 "", size=size@entry=971)
    at libavformat/tcp.c:172
#4  0x000000000052efc8 in retry_transfer_wrapper (
    transfer_func=0x620c30 <tcp_read>, size_min=1024, size=1024, 
    buf=0x7fffea867c10 "ytI8KQRmjSJ_\r\nDate: Wed, 29 Jul 2015 23:56:02 GMT\r\n\r\n", h=0x7fffe40057e0)
    at libavformat/avio.c:306
#5  ffurl_read_complete (h=0x7fffe40057e0, 
    buf=buf@entry=0x7fffea867c10 "ytI8KQRmjSJ_\r\nDate: Wed, 29 Jul 2015 23:56:02 GMT\r\n\r\n", size=size@entry=1024)
    at libavformat/avio.c:344
#6  0x000000000060712e in ff_rtsp_skip_packet (s=0x7fffe4005ca0)
    at libavformat/rtsp.c:1123
#7  ff_rtsp_read_reply (s=s@entry=0x7fffe4005ca0, 
    reply=reply@entry=0x7fffea86a6e0, content_ptr=content_ptr@entry=0x0, 
    return_on_interleaved_data=return_on_interleaved_data@entry=0, 
    method=method@entry=0xe58e1a "SETUP")
    at libavformat/rtsp.c:1163
#8  0x0000000000608833 in ff_rtsp_send_cmd_with_content (
    send_content_length=0, send_content=0x0, content_ptr=0x0, 
    reply=0x7fffea86a6e0, 
    header=0x7fffea8696e0 "Transport: RTP/AVP/UDP;unicast;client_port=17082-17083\r\n", url=0x7fffe4004e1c "rtsp://127.0.0.1:8554/test/stream=0", 
    method=0xe58e1a "SETUP", s=0x7fffe4005ca0)
    at libavformat/rtsp.c:1372
#9  ff_rtsp_send_cmd (content_ptr=0x0, reply=0x7fffea86a6e0, 
    headers=0x7fffea8696e0 "Transport: RTP/AVP/UDP;unicast;client_port=17082-17083\r\n", url=0x7fffe4004e1c "rtsp://127.0.0.1:8554/test/stream=0", 
    method=0xe58e1a "SETUP", s=0x7fffe4005ca0)
    at libavformat/rtsp.c:1349
#10 ff_rtsp_make_setup_request (s=s@entry=0x7fffe4005ca0, 
    host=host@entry=0x7fffea86c420 "127.0.0.1", port=<optimized out>, 
    lower_transport=lower_transport@entry=0, real_challenge=0x0)
    at libavformat/rtsp.c:1532
#11 0x0000000000609788 in ff_rtsp_connect (s=s@entry=0x7fffe4005ca0)
    at libavformat/rtsp.c:1868
#12 0x000000000060bdb8 in rtsp_read_header (s=0x7fffe4005ca0)
    at libavformat/rtspdec.c:726
#13 0x0000000000632bf3 in avformat_open_input (ps=ps@entry=0x7fffea86fc30, 
    filename=filename@entry=0x7ffff7f25c64 "rtsp://127.0.0.1:8554/test", 
    fmt=<optimized out>, options=0x19b9528 <format_opts>)
    at libavformat/utils.c:470
#14 0x000000000047bf56 in read_thread (arg=0x7ffff7e24040)
    at ffplay.c:2769
#15 0x00007ffff66ad2f8 in ?? () 
#16 0x00007ffff66efe99 in ?? () 
#17 0x00007ffff5b350a4 in start_thread (arg=0x7fffea870700)
#18 0x00007ffff586a07d in clone ()

in reply to:  2 comment:4 by kuroneko, 9 years ago

Replying to cehoyos:

Then please test current FFmpeg git head and provide a backtrace when FFmpeg hangs.

OK, first attempt (todays head of git, https://github.com/FFmpeg/FFmpeg/commit/f977e69dacf092af3741abebd20239fa10b107c3)

Admittedly I used ffplay for testing but the issue is the same. When it stops it simply sits there.

server response to first SETUP call

RTSP/1.0 200 OK
CSeq: 4
Transport: RTP/AVP;unicast;client_port=34316-34317;server_port=50000-50001;ssrc=57C96FAA;mode="PLAY"
Server: GStreamer RTSP server
Session: $U-DmSGQQvPJXZRI; timeout=60
Date: Thu, 30 Jul 2015 07:08:36 GMT

info threads

  Id   Target Id         Frame 
  5    Thread 0x7fffec749700 (LWP 2679) "ffplay_g" 0x000000392deea8ad in poll () from /lib64/libc.so.6
  3    Thread 0x7ffff0f4b700 (LWP 2677) "ffplay_g" 0x000000392e60ef3d in nanosleep () from /lib64/libpthread.so.0
  2    Thread 0x7ffff7dc0700 (LWP 2676) "ffplay_g" 0x000000392e60ef3d in nanosleep () from /lib64/libpthread.so.0
* 1    Thread 0x7ffff7dc1740 (LWP 2675) "ffplay_g" 0x000000392e60ef3d in nanosleep () from /lib64/libpthread.so.0

backtrace for thread 5

#0  0x000000392deea8ad in poll () from /lib64/libc.so.6
#1  0x0000000000554054 in ff_network_wait_fd (fd=<optimized out>, fd=<optimized out>, write=<optimized out>) at libavformat/network.c:78
#2  ff_network_wait_fd_timeout (fd=5, write=<optimized out>, timeout=<optimized out>, int_cb=0x7fffe40057b0) at libavformat/network.c:90
#3  0x00000000005a24db in tcp_read (h=<optimized out>, buf=0x7fffec737841 "", size=959) at libavformat/tcp.c:172
#4  0x00000000004cac87 in retry_transfer_wrapper (h=<optimized out>, buf=<optimized out>, size_min=<optimized out>, transfer_func=<optimized out>, 
    h=<optimized out>, buf=<optimized out>, size=<optimized out>, size_min=<optimized out>, transfer_func=<optimized out>) at libavformat/avio.c:306
#5  ffurl_read_complete (h=0x7fffe4005780, buf=0x7fffec737800 "mSGQQvPJXZRI; timeout=60\r\nDate: Thu, 30 Jul 2015 07:08:36 GMT\r\n\r\n", size=1024)
    at libavformat/avio.c:344
#6  0x0000000000587e03 in ff_rtsp_skip_packet (s=<optimized out>, s=<optimized out>) at libavformat/rtsp.c:1123
#7  ff_rtsp_read_reply (s=0x7fffe40008c0, reply=0x7fffec739788, content_ptr=0x0, return_on_interleaved_data=0, method=0xe475ac "SETUP") at libavformat/rtsp.c:1163
#8  0x000000000058846a in ff_rtsp_send_cmd_with_content (s=0x7fffe40008c0, method=0xe475ac "SETUP", 
    url=0x7fffe4004a1c "rtsp://192.168.11.118:554/axis-media/media.amp/stream=0", 
    header=0x7fffec738f80 "Transport: RTP/AVP/UDP;unicast;client_port=34316-34317\r\n", reply=0x7fffec739788, content_ptr=0x0, send_content=<optimized out>, 
    send_content_length=<optimized out>) at libavformat/rtsp.c:1372
#9  0x0000000000588f42 in ff_rtsp_send_cmd (s=0x7fffec735970, method=0x1 <error: Cannot access memory at address 0x1>, 
    url=0x64 <error: Cannot access memory at address 0x64>, headers=0xffffffffffffffff <error: Cannot access memory at address 0xffffffffffffffff>, reply=0x0, 
    content_ptr=0x0, s=0x7fffec735970, method=0x1 <error: Cannot access memory at address 0x1>, url=0x64 <error: Cannot access memory at address 0x64>, 
    headers=0xffffffffffffffff <error: Cannot access memory at address 0xffffffffffffffff>, reply=0x0, content_ptr=0x0) at libavformat/rtsp.c:1349
#10 ff_rtsp_make_setup_request (s=0x7fffe40008c0, host=0x7fffec73e7d0 "192.168.11.118", port=<optimized out>, lower_transport=0, real_challenge=0x0)
    at libavformat/rtsp.c:1532
#11 0x00000000005897ae in ff_rtsp_connect (s=0x7fffe40008c0) at libavformat/rtsp.c:1868
#12 0x000000000058b76e in rtsp_read_header (s=0x7fffe40008c0) at libavformat/rtspdec.c:726
#13 0x00000000005a94d6 in avformat_open_input (ps=0x7fffec748e40, filename=<optimized out>, fmt=<optimized out>, options=0x1941a68 <format_opts>)
    at libavformat/utils.c:470
#14 0x00000000004140a5 in read_thread (arg=0x7ffff0648040) at ffplay.c:2769
#15 0x0000003c5b811598 in SDL_RunThread () from /lib64/libSDL-1.2.so.0
#16 0x0000003c5b854d99 in RunThread () from /lib64/libSDL-1.2.so.0
#17 0x000000392e607ee5 in start_thread () from /lib64/libpthread.so.0
#18 0x000000392def4d1d in clone () from /lib64/libc.so.6

relevant section from ff_rtsp_read_reply()

    /* parse reply (XXX: use buffers) */
    rt->last_reply[0] = '\0';
    for (;;) {
        q = buf;
        for (;;) {
            ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1);
            av_log(s, AV_LOG_TRACE, "ret=%d c=%02x [%c]\n", ret, ch, ch);
            if (ret != 1)
                return AVERROR_EOF;
            if (ch == '\n')
                break;
            if (ch == '$') {
                /* XXX: only parse it if first char on line ? */
                if (return_on_interleaved_data) {
                    return 1;
                } else
                    ff_rtsp_skip_packet(s);
            } else if (ch != '\r') {
                if ((q - buf) < sizeof(buf) - 1)
                    *q++ = ch;
            }
        }

I just had a quick look through the RFCs. Transport: is set to not allow TCP data to be interleaved (in my case) in which case I would expect that $ is not treated specially. If interleaving is used then the delimiter $ only occurs at the beginning of a block.

     C->S: PLAY rtsp://foo.com/bar.file RTSP/1.0
           CSeq: 3
           Session: 12345678

     S->C: RTSP/1.0 200 OK
           CSeq: 3
           Session: 12345678
           Date: 05 Jun 1997 18:59:15 GMT
           RTP-Info: url=rtsp://foo.com/bar.file;
             seq=232433;rtptime=972948234

     S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
     S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
     S->C: $\001{2 byte length}{"length" bytes  RTCP packet}
Last edited 9 years ago by kuroneko (previous) (diff)

comment:5 by kuroneko, 9 years ago

Cc: marko.lukat@kyi.biglobe.ne.jp added

comment:6 by kuroneko, 9 years ago

This is issue has been fixed for me by applying https://github.com/libav/libav/commit/764ec70149728be82304c163ccc4e280f1629201 locally.

comment:7 by Carl Eugen Hoyos, 9 years ago

Component: undeterminedavformat
Resolution: fixed
Status: newclosed
Version: unspecifiedgit-master

Merged as bc4bdc6d61527e9e1bd2ff6d39ee68eac68ff9de
Thank you for testing!

Note: See TracTickets for help on using tickets.