Date: 2026-08-28 SDK: AmebaPro2 (Arduino 4.1.0 / ameba-rtos-pro2) File: component/network/rtsp/rtsp_api.c
- 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_NUMand hit this. - The failure mode is quiet. There is no crash, no error return and no log line; only a
server_portof 0 in aSETUPresponse, 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_sessionrather 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.