webrtcbin: Reject answers that don't contain the same number of m-line as offer

Otherwise, it segfaults later. Also add test to validate this.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2561>
This commit is contained in:
Olivier Crête 2022-05-30 16:31:38 -04:00 committed by GStreamer Marge Bot
parent 56451b0f1a
commit e26bd431ad
2 changed files with 116 additions and 9 deletions

View file

@ -5396,18 +5396,16 @@ done:
return ret;
}
static gboolean
check_transceivers_not_removed (GstWebRTCBin * webrtc,
static gint
transceivers_media_num_cmp (GstWebRTCBin * webrtc,
GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
{
if (!previous)
return TRUE;
return 0;
if (gst_sdp_message_medias_len (previous->sdp) >
gst_sdp_message_medias_len (new->sdp))
return FALSE;
return gst_sdp_message_medias_len (new->sdp) -
gst_sdp_message_medias_len (previous->sdp);
return TRUE;
}
static gboolean
@ -5495,6 +5493,35 @@ get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
return NULL;
}
static GstWebRTCSessionDescription *
get_last_generated_description (GstWebRTCBin * webrtc, SDPSource source,
GstWebRTCSDPType type)
{
switch (type) {
case GST_WEBRTC_SDP_TYPE_OFFER:
if (source == SDP_REMOTE)
return webrtc->priv->last_generated_answer;
else
return webrtc->priv->last_generated_offer;
break;
case GST_WEBRTC_SDP_TYPE_PRANSWER:
case GST_WEBRTC_SDP_TYPE_ANSWER:
if (source == SDP_LOCAL)
return webrtc->priv->last_generated_answer;
else
return webrtc->priv->last_generated_offer;
case GST_WEBRTC_SDP_TYPE_ROLLBACK:
return NULL;
default:
/* other values mean memory corruption/uninitialized! */
g_assert_not_reached ();
break;
}
return NULL;
}
/* http://w3c.github.io/webrtc-pc/#set-description */
static GstStructure *
_set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
@ -5535,9 +5562,9 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
}
}
if (!check_transceivers_not_removed (webrtc,
if (transceivers_media_num_cmp (webrtc,
get_previous_description (webrtc, sd->source, sd->sdp->type),
sd->sdp)) {
sd->sdp) < 0) {
g_set_error_literal (&error, GST_WEBRTC_ERROR,
GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"m=lines removed from the SDP. Processing a completely new connection "
@ -5545,6 +5572,17 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
goto out;
}
if ((sd->sdp->type == GST_WEBRTC_SDP_TYPE_PRANSWER ||
sd->sdp->type == GST_WEBRTC_SDP_TYPE_ANSWER) &&
transceivers_media_num_cmp (webrtc,
get_last_generated_description (webrtc, sd->source, sd->sdp->type),
sd->sdp) != 0) {
g_set_error_literal (&error, GST_WEBRTC_ERROR,
GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
"Answer doesn't have the same number of m-lines as the offer.");
goto out;
}
if (!check_locked_mlines (webrtc, sd->sdp, &error))
goto out;

View file

@ -4371,6 +4371,74 @@ GST_START_TEST (test_codec_preferences_in_on_new_transceiver)
GST_END_TEST;
static void
add_media_line (struct test_webrtc *t, GstElement * element,
GstWebRTCSessionDescription * desc, gpointer user_data)
{
GstSDPMedia *media = NULL;
const GstSDPMedia *existing_media;
GstSDPResult res;
existing_media = gst_sdp_message_get_media (desc->sdp, 0);
res = gst_sdp_media_copy (existing_media, &media);
fail_unless (res == GST_SDP_OK);
res = gst_sdp_message_add_media (desc->sdp, media);
fail_unless (res == GST_SDP_OK);
gst_sdp_media_free (media);
}
static void
on_answer_set_rejected (struct test_webrtc *t, GstElement * element,
GstPromise * promise, gpointer user_data)
{
const GstStructure *s;
GError *error = NULL;
GError *compare_error = user_data;
s = gst_promise_get_reply (promise);
fail_unless (s != NULL);
gst_structure_get (s, "error", G_TYPE_ERROR, &error, NULL);
fail_unless (g_error_matches (error, compare_error->domain,
compare_error->code));
fail_unless_equals_string (compare_error->message, error->message);
g_clear_error (&error);
}
GST_START_TEST (test_invalid_add_media_in_answer)
{
struct test_webrtc *t = create_audio_test ();
VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads,
NULL, NULL);
guint media_format_count[] = { 1 };
VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
media_format_count, &no_duplicate_payloads);
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (1),
&media_formats);
const gchar *expected_offer_setup[] = { "actpass", };
VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &count);
const gchar *expected_offer_direction[] = { "sendrecv", };
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction,
&offer_setup);
VAL_SDP_INIT (answer, add_media_line, NULL, NULL);
GError answer_set_error = { GST_WEBRTC_ERROR,
GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
(gchar *) "Answer doesn't have the same number of m-lines as the offer."
};
/* Ensure that if the answer has more m-lines than the offer, it gets
* rejected.
*/
t->on_answer_set = on_answer_set_rejected;
t->answer_set_data = &answer_set_error;
test_validate_sdp (t, &offer, &answer);
test_webrtc_free (t);
}
GST_END_TEST;
static Suite *
webrtcbin_suite (void)
{
@ -4425,6 +4493,7 @@ webrtcbin_suite (void)
tcase_add_test (tc, test_codec_preferences_no_duplicate_extmaps);
tcase_add_test (tc, test_codec_preferences_incompatible_extmaps);
tcase_add_test (tc, test_codec_preferences_invalid_extmap);
tcase_add_test (tc, test_invalid_add_media_in_answer);
if (sctpenc && sctpdec) {
tcase_add_test (tc, test_data_channel_create);
tcase_add_test (tc, test_data_channel_remote_notify);