[RTL8735B / AmebaPro2] rtsp_transport_init() writes past the end of transport[]

Date: 2026-08-28 SDK: AmebaPro2 (Arduino 4.1.0 / ameba-rtos-pro2) File: component/network/rtsp/rtsp_api.c

  1. Summary

rtsp_transport_init() advances its pointer with += i instead of ++, so it visits array indices 0, 1, 3, 6, 10 … rather than 0, 1, 2, 3 …

With the shipped RTSP_MAX_STREAM_NUM of 2 the loop happens to visit exactly [0] and [1] and the defect is invisible. Any project that raises RTSP_MAX_STREAM_NUM gets an out-of-bounds write on the third iteration and an uninitialised transport[2].

We raised it to 3 to add an metadata track, which is how we met this.


2. The code

c

void rtsp_transport_init(struct rtsp_context *rtsp_ctx)
{
    struct rtsp_transport *transport = &rtsp_ctx->transport[0];
    for (int i = 0; i < RTSP_MAX_STREAM_NUM; i++) {
        transport += i;                 /* <-- accumulates */
        transport->serverport_low  = rtsp_ctx->id * 2 + RTP_SERVER_PORT_BASE;
        transport->serverport_high = rtsp_ctx->id * 2 + RTP_SERVER_PORT_BASE + 1;
        transport->port_low        = rtsp_ctx->id * 2 + RTP_PORT_BASE;
        transport->port_high       = rtsp_ctx->id * 2 + RTP_PORT_BASE + 1;
        transport->clientport_low  = rtsp_ctx->id * 2 + RTP_CLIENT_PORT_BASE;
        transport->clientport_high = rtsp_ctx->id * 2 + RTP_CLIENT_PORT_BASE + 1;
        transport->isRtp    = 1;
        transport->isTcp    = 0;
        transport->castMode = UNICAST_UDP_MODE;
        transport->ttl      = 0;
    }
}

Pointer positions per iteration:

i = 0   transport += 0   ->  [0]     initialised
i = 1   transport += 1   ->  [1]     initialised
i = 2   transport += 2   ->  [3]     out of bounds; [2] never initialised
i = 3   transport += 3   ->  [6]

3. What the out-of-bounds write lands on

rtsp_api.h:

c

struct rtsp_context {
    ...
    struct rtsp_transport transport[RTSP_MAX_STREAM_NUM];
    struct rtsp_session   session;          /* <-- immediately after */
    u16 rtpseq[RTSP_MAX_STREAM_NUM];
    ...
};

With RTSP_MAX_STREAM_NUM = 3, the third iteration writes ten fields into the memory occupied by struct rtsp_session.


4. How it surfaced

We added an ONVIF metadata track as a third stream. Every device-side probe reported success — the stream was configured, the RTP handler ran, sendto() returned without error — and the ONVIF Device Test Tool counted zero metadata frames.

The two SETUP responses side by side named the cause:

video track       Transport: RTP/AVP/UDP;unicast;client_port=52578-52579;server_port=55608-55609
metadata track    Transport: RTP/AVP/UDP;unicast;client_port=53274-53275;server_port=0-0

transport[2] was still zero-filled, so the device advertised that it would send from port 0. The client was therefore never listening where the packets actually came from.


5. Suggested fix

c

void rtsp_transport_init(struct rtsp_context *rtsp_ctx)
{
    struct rtsp_transport *transport;
    for (int i = 0; i < RTSP_MAX_STREAM_NUM; i++) {
        transport = &rtsp_ctx->transport[i];
        ...
    }
}

Indexing rather than incrementing also means the loop cannot drift if RTSP_MAX_STREAM_NUM changes again.


6. Why this may be worth fixing upstream even at MAX = 2

  • ONVIF Profile T §7.13 makes metadata streaming mandatory, and metadata needs a third stream alongside video and audio. Any customer pursuing Profile T certification on this SDK will raise RTSP_MAX_STREAM_NUM and hit this.
  • The failure mode is quiet. There is no crash, no error return and no log line; only a server_port of 0 in a SETUP response, and a stream that transports nothing.
  • The out-of-bounds write is into a live structure in the same object, so its effects would depend on the layout of struct rtsp_session rather than faulting.

7. Related observation (lower priority, separate item)

sdp_fill_m_field() is called with the payload type computed as

c

(pt >= RTP_PT_DYN_BASE) ? (pt + stream_id) : pt

while create_sdp_a_string() writes a bare codec->pt into a=rtpmap, and each codec’s RTP handler puts codec->pt on the wire.

These agree today only because the single dynamic payload type in use (H.264, 96) always sits at stream_id 0, so 96 + 0 == 96. A dynamic payload type on any other stream index produces an m= line, an a=rtpmap line and an RTP header that disagree.

We hit this three times while adding the metadata track and corrected each of them locally. It may be worth making the three sites share one expression.

:waving_hand: Thanks for your post!

For documentation, SDK resources, FAQs, and community guidelines, please visit: here

Happy building with Ameba!


:waving_hand: 感谢您的发帖!

如需查阅官方文档、SDK 资源、常见问题(FAQ)及社区使用指南,请参考: 這裏

祝您使用 Ameba 开发愉快!