[vlc-devel] [PATCH] lib: media: rewrite recursion into list exhaustion
Alexandre Janniaux
ajanni at videolabs.io
Tue Jul 14 22:18:05 CEST 2020
As the comment described it, recursion could lead to a stack overflow
when parsing very long recursive playlist. Instead, use temporary
dynamic allocation to chain the addition requests and process them in
the same order.
The out-of-memory errors are only returned to the user as a log and the
preparsed playlist item is truncated, which is non-optimal.
---
lib/media.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 78 insertions(+), 11 deletions(-)
diff --git a/lib/media.c b/lib/media.c
index 55f30246229..8dee1ca50cb 100644
--- a/lib/media.c
+++ b/lib/media.c
@@ -181,19 +181,90 @@ static libvlc_media_t *input_item_add_subitem( libvlc_media_t *p_md,
return p_md_child;
}
+struct vlc_item_list
+{
+ struct vlc_list node;
+ input_item_node_t *item;
+ libvlc_media_t *media;
+};
+
+static struct vlc_item_list *
+wrap_item_in_list( libvlc_media_t *media, input_item_node_t *item )
+{
+ struct vlc_item_list *node = malloc( sizeof *node );
+ if( node == NULL )
+ return NULL;
+ node->item = item;
+ node->media = media;
+ return node;
+}
+
static void input_item_add_subnode( libvlc_media_t *md,
- input_item_node_t *node )
+ input_item_node_t *root )
{
- for( int i = 0; i < node->i_children; i++ )
+ struct vlc_list list;
+ vlc_list_init( &list );
+
+ /* Retain the media because we don't want the search algorithm to release
+ * it when its subitems get parsed. */
+ libvlc_media_retain(md);
+
+ struct vlc_item_list *node_root = wrap_item_in_list( md, root );
+ if( node_root == NULL )
+ {
+ libvlc_media_release(md);
+ goto error;
+ }
+
+ /* This is a depth-first search algorithm, so stash the root of the tree
+ * first, then stash its children and loop back on the last item in the
+ * list until the full subtree is parsed, and eventually the full tree is
+ * parsed. */
+ vlc_list_append( &node_root->node, &list );
+
+ while( !vlc_list_is_empty( &list ) )
{
- input_item_node_t *child = node->pp_children[i];
- libvlc_media_t *md_child = input_item_add_subitem( md, child->p_item );
+ /* Pop last item in the list. */
+ struct vlc_item_list *node =
+ vlc_list_last_entry_or_null( &list, struct vlc_item_list, node );
+ vlc_list_remove(&node->node);
- if( md_child != NULL )
+ for( int i = 0; i < node->item->i_children; i++ )
{
- input_item_add_subnode( md_child, child );
- libvlc_media_release( md_child );
+ input_item_node_t *child = node->item->pp_children[i];
+
+ /* The media will be released when its children will be added to
+ * the list. */
+ libvlc_media_t *md_child = input_item_add_subitem( node->media, child->p_item );
+ if( md_child == NULL )
+ goto error;
+
+ struct vlc_item_list *submedia =
+ wrap_item_in_list( md_child, child );
+ if (submedia == NULL)
+ {
+ libvlc_media_release( md_child );
+ goto error;
+ }
+
+ /* Stash a request to parse this subtree. */
+ vlc_list_append( &submedia->node, &list );
}
+
+ libvlc_media_release( node->media );
+ free( node );
+ }
+ return;
+
+error:
+ libvlc_printerr( "Not enough memory" );
+
+ struct vlc_item_list *node;
+ vlc_list_foreach( node, &list, node )
+ {
+ if( node->media != NULL )
+ libvlc_media_release( node->media );
+ free( node );
}
}
@@ -212,10 +283,6 @@ static void input_item_subtree_added(input_item_t *item,
void libvlc_media_add_subtree(libvlc_media_t *p_md, input_item_node_t *node)
{
- /* FIXME FIXME FIXME
- * Recursive function calls seem much simpler for this. But playlists are
- * untrusted and can be arbitrarily deep (e.g. with XSPF). So recursion can
- * potentially lead to plain old stack overflow. */
input_item_add_subnode( p_md, node );
/* Construct the event */
--
2.27.0
More information about the vlc-devel
mailing list