[vlc-commits] [Git][videolan/vlc][master] 6 commits: sout: hls: fix block chain leak on error

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu Apr 2 16:46:15 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
8fbb4d5a by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: fix block chain leak on error

- - - - -
40d41133 by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: avoid potential NULL deref

Storage is NULL guarded in the error handling, not defining it after
malloc leads to UB.

- - - - -
baf40934 by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: storage: fix invalid free

The underlying pointer must be freed, this was a typo.

- - - - -
4c3774b4 by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: storage: propagate error codes to callers

This will allow better error logging from the caller, we previously had
no idea of the error causes, leading to un-helpful logs.

- - - - -
6c08feff by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: propagate segment creation errors

Better propagate fs underlying errors, in order to improve logging and
failure reasons.

- - - - -
9e0ea06c by Alaric Senat at 2026-04-02T16:32:19+00:00
sout: hls: improve storage creation logging

Add logs and reasons to manifest and segment creation failures.
Previously, no error was displayed if the files could not be created for
whatever reasons.

- - - - -


5 changed files:

- modules/stream_out/hls/hls.c
- modules/stream_out/hls/segments.c
- modules/stream_out/hls/segments.h
- modules/stream_out/hls/storage.c
- modules/stream_out/hls/storage.h


Changes:

=====================================
modules/stream_out/hls/hls.c
=====================================
@@ -277,7 +277,8 @@ error:
     return NULL;
 }
 
-static struct hls_storage *GenerateMainManifest(const sout_stream_sys_t *sys)
+static int GenerateMainManifest(const sout_stream_sys_t *sys,
+                                struct hls_storage **storage_out)
 {
     struct vlc_memstream out;
     vlc_memstream_open(&out);
@@ -384,23 +385,23 @@ static struct hls_storage *GenerateMainManifest(const sout_stream_sys_t *sys)
 #undef MANIFEST_END_TAG
 
     if (vlc_memstream_close(&out) != 0)
-        return NULL;
+        return -ENOMEM;
 
     const struct hls_storage_config storage_conf = {
         .name = "index.m3u8",
         .mime = "application/vnd.apple.mpegurl",
     };
     return hls_storage_FromBytes(
-        out.ptr, out.length, &storage_conf, &sys->config);
+        out.ptr, out.length, &storage_conf, &sys->config, storage_out);
 error:
-    if (vlc_memstream_close(&out) != 0)
-        return NULL;
-    free(out.ptr);
-    return NULL;
+    if (vlc_memstream_close(&out) == 0)
+        free(out.ptr);
+    return -ENOMEM;
 }
 
-static struct hls_storage *
-GeneratePlaylistManifest(const hls_playlist_t *playlist)
+static int
+GeneratePlaylistManifest(const hls_playlist_t *playlist,
+                         struct hls_storage **storage_out)
 {
     struct vlc_memstream out;
     vlc_memstream_open(&out);
@@ -442,24 +443,28 @@ GeneratePlaylistManifest(const hls_playlist_t *playlist)
 #undef MANIFEST_ADD_TAG
 
     if (vlc_memstream_close(&out) != 0)
-        return NULL;
+        return -ENOMEM;
 
     const struct hls_storage_config storage_config = {
         .name = playlist->name, .mime = "application/vnd.apple.mpegurl"};
     return hls_storage_FromBytes(
-        out.ptr, out.length, &storage_config, playlist->config);
+        out.ptr, out.length, &storage_config, playlist->config, storage_out);
 error:
-    if (vlc_memstream_close(&out) != 0)
-        return NULL;
-    free(out.ptr);
-    return NULL;
+    if (vlc_memstream_close(&out) == 0)
+        free(out.ptr);
+    return -ENOMEM;
 }
 
 static int UpdatePlaylistManifest(hls_playlist_t *playlist)
 {
-    struct hls_storage *new_manifest = GeneratePlaylistManifest(playlist);
-    if (unlikely(new_manifest == NULL))
-        return VLC_EGENERIC;
+    struct hls_storage *new_manifest;
+    const int ret = GeneratePlaylistManifest(playlist, &new_manifest);
+    if (unlikely(ret != VLC_SUCCESS))
+    {
+        vlc_error(playlist->logger, "Failed to update playlist manifest: %s",
+                  vlc_strerror(-ret));
+        return ret;
+    }
 
     if (playlist->http_manifest != NULL)
     {
@@ -579,8 +584,9 @@ static int ExtractAndAddSegment(hls_playlist_t *playlist,
     if (unlikely(status != VLC_SUCCESS))
     {
         vlc_error(playlist->logger,
-                  "Segment '%u' creation failed",
-                  playlist->segments.total_segments + 1);
+                  "Segment '%u' creation failed: %s",
+                  playlist->segments.total_segments + 1,
+                  vlc_strerror(-status));
         return status;
     }
     playlist->muxed_duration += length;
@@ -893,9 +899,12 @@ Add(sout_stream_t *stream, const es_format_t *fmt, const char *es_id)
 
     vlc_list_append(&track->node, &playlist->tracks);
 
-    struct hls_storage *new_manifest = GenerateMainManifest(sys);
-    if (unlikely(new_manifest == NULL))
+    struct hls_storage *new_manifest;
+    const int manifest_ret = GenerateMainManifest(sys, &new_manifest);
+    if (unlikely(manifest_ret != VLC_SUCCESS))
     {
+        msg_Err(stream, "Failed to generate main manifest: %s",
+                vlc_strerror(-manifest_ret));
         vlc_list_remove(&track->node);
         free(track);
         goto error;


=====================================
modules/stream_out/hls/segments.c
=====================================
@@ -25,6 +25,7 @@
 
 #include <vlc_common.h>
 
+#include <vlc_block.h>
 #include <vlc_httpd.h>
 #include <vlc_list.h>
 #include <vlc_tick.h>
@@ -86,10 +87,16 @@ int hls_segment_queue_NewSegment(hls_segment_queue_t *queue,
 {
     hls_segment_t *segment = malloc(sizeof(*segment));
     if (unlikely(segment == NULL))
-        return VLC_ENOMEM;
+    {
+        block_ChainRelease(content);
+        return -ENOMEM;
+    }
 
     segment->id = queue->total_segments;
     segment->length = length;
+    segment->storage = NULL;
+    segment->http_url = NULL;
+    int ret = -ENOMEM;
 
     if (asprintf(&segment->url,
                  "%s/playlist-%u-%u.%s",
@@ -99,6 +106,7 @@ int hls_segment_queue_NewSegment(hls_segment_queue_t *queue,
                  queue->file_extension) == -1)
     {
         segment->url = NULL;
+        block_ChainRelease(content);
         goto nomem;
     }
 
@@ -106,10 +114,10 @@ int hls_segment_queue_NewSegment(hls_segment_queue_t *queue,
         .name = segment->url + strlen(queue->hls_config->base_url) + 1,
         .mime = "video/MP2T",
     };
-    segment->storage =
-        hls_storage_FromBlocks(content, &storage_conf, queue->hls_config);
-    if (unlikely(segment->storage == NULL))
-        goto nomem;
+    ret = hls_storage_FromBlocks(
+        content, &storage_conf, queue->hls_config, &segment->storage);
+    if (unlikely(ret != 0))
+        goto err;
 
     if (queue->httpd_ref != NULL)
     {
@@ -123,8 +131,6 @@ int hls_segment_queue_NewSegment(hls_segment_queue_t *queue,
                        queue->httpd_callback,
                        (httpd_callback_sys_t *)segment->storage);
     }
-    else
-        segment->http_url = NULL;
 
     if (hls_segment_queue_IsAtMaxCapacity(queue))
     {
@@ -138,9 +144,11 @@ int hls_segment_queue_NewSegment(hls_segment_queue_t *queue,
     vlc_list_append(&segment->priv_node, &queue->segments);
     return VLC_SUCCESS;
 nomem:
+    ret = -ENOMEM;
+err:
     if (segment->storage != NULL)
         hls_storage_Destroy(segment->storage);
     free(segment->url);
     free(segment);
-    return VLC_ENOMEM;
+    return ret;
 }


=====================================
modules/stream_out/hls/segments.h
=====================================
@@ -82,7 +82,7 @@ void hls_segment_queue_Clear(hls_segment_queue_t *);
  * \param length The media time size of the segment.
  *
  * \retval VLC_SUCCESS on success.
- * \retval VLC_ENOMEM on internal allocation failure.
+ * \retval -errno on error.
  */
 int hls_segment_queue_NewSegment(hls_segment_queue_t *,
                                  block_t *content,


=====================================
modules/stream_out/hls/storage.c
=====================================
@@ -79,37 +79,39 @@ static ssize_t mem_storage_GetContent(const hls_storage_t *storage,
     return priv->size;
 }
 
-static hls_storage_t *mem_storage_FromBlock(block_t *content)
+static int mem_storage_FromBlock(block_t *content, hls_storage_t **out)
 {
     struct storage_priv *priv = malloc(sizeof(*priv));
     if (unlikely(priv == NULL))
-        return NULL;
+        return -ENOMEM;
 
     priv->storage.get_content = mem_storage_GetContent;
     priv->destroy = mem_storage_Destroy;
     priv->mem.content = content;
     block_ChainProperties(content, NULL, &priv->size, NULL);
-    return &priv->storage;
+    *out = &priv->storage;
+    return 0;
 }
 
-static hls_storage_t *mem_storage_FromBytes(void *bytes, size_t size)
+static int mem_storage_FromBytes(void *bytes, size_t size, hls_storage_t **out)
 {
     struct storage_priv *priv = malloc(sizeof(*priv));
     if (unlikely(priv == NULL))
-        return NULL;
+        return -ENOMEM;
 
     block_t *content = block_heap_Alloc(bytes, size);
     if (unlikely(content == NULL))
     {
         free(priv);
-        return NULL;
+        return -ENOMEM;
     }
 
     priv->storage.get_content = mem_storage_GetContent;
     priv->destroy = mem_storage_Destroy;
     priv->size = size;
     priv->mem.content = content;
-    return &priv->storage;
+    *out = &priv->storage;
+    return 0;
 }
 
 static ssize_t fs_storage_Read(int fd, uint8_t buf[], size_t len)
@@ -139,7 +141,6 @@ static ssize_t fs_storage_GetContent(const hls_storage_t *storage,
         container_of(storage, struct storage_priv, storage);
 
     const int fd = vlc_open(priv->fs.path, O_RDONLY);
-
     if (fd == -1)
         return -1;
 
@@ -154,7 +155,7 @@ static ssize_t fs_storage_GetContent(const hls_storage_t *storage,
     close(fd);
     return read;
 err:
-    free(dest);
+    free(*dest);
     close(fd);
     return -1;
 }
@@ -169,12 +170,12 @@ static int fs_storage_Write(int fd, const uint8_t *data, size_t len)
         {
             if (errno == EINTR || errno == EAGAIN)
                 continue;
-            return VLC_EGENERIC;
+            return -errno;
         }
 
         written += n;
     }
-    return VLC_SUCCESS;
+    return 0;
 }
 
 static void fs_storage_Destroy(struct storage_priv *priv)
@@ -193,28 +194,40 @@ static inline char *fs_storage_CreatePath(const char *outdir,
     return ret;
 }
 
-static hls_storage_t *
+static int
 fs_storage_FromBlock(block_t *content,
                      const struct hls_storage_config *config,
-                     const struct hls_config *hls_config)
+                     const struct hls_config *hls_config,
+                     hls_storage_t **out)
 {
+    int ret;
+
     struct storage_priv *priv = malloc(sizeof(*priv));
     if (unlikely(priv == NULL))
-        goto err;
+    {
+        block_ChainRelease(content);
+        return -ENOMEM;
+    }
 
     priv->fs.path = fs_storage_CreatePath(hls_config->outdir, config->name);
     if (unlikely(priv->fs.path == NULL))
+    {
+        ret = -ENOMEM;
         goto err;
+    }
 
     const int fd = vlc_open(priv->fs.path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
     if (fd == -1)
+    {
+        ret = -errno;
         goto err;
+    }
 
     size_t size = 0;
     for (const block_t *it = content; it != NULL; it = it->p_next)
     {
-        const int status = fs_storage_Write(fd, it->p_buffer, it->i_buffer);
-        if (status != VLC_SUCCESS)
+        ret = fs_storage_Write(fd, it->p_buffer, it->i_buffer);
+        if (ret != 0)
         {
             close(fd);
             goto err;
@@ -229,37 +242,49 @@ fs_storage_FromBlock(block_t *content,
     priv->size = size;
     priv->destroy = fs_storage_Destroy;
 
-    return &priv->storage;
+    *out = &priv->storage;
+    return 0;
 err:
     block_ChainRelease(content);
-    if (priv != NULL)
-        free(priv->fs.path);
+    free(priv->fs.path);
     free(priv);
-    return NULL;
+    return ret;
 }
 
-static hls_storage_t *
+static int
 fs_storage_FromBytes(void *bytes,
                      size_t size,
                      const struct hls_storage_config *config,
-                     const struct hls_config *hls_config)
+                     const struct hls_config *hls_config,
+                     hls_storage_t **out)
 {
+    int ret;
+
     struct storage_priv *priv = malloc(sizeof(*priv));
     if (unlikely(priv == NULL))
-        return NULL;
+    {
+        free(bytes);
+        return -ENOMEM;
+    }
 
     priv->fs.path = fs_storage_CreatePath(hls_config->outdir, config->name);
     if (unlikely(priv->fs.path == NULL))
+    {
+        ret = -ENOMEM;
         goto err;
+    }
 
     const int fd = vlc_open(priv->fs.path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
     if (fd == -1)
+    {
+        ret = -errno;
         goto err;
+    }
 
-    const int status = fs_storage_Write(fd, bytes, size);
+    ret = fs_storage_Write(fd, bytes, size);
     close(fd);
 
-    if (unlikely(status != VLC_SUCCESS))
+    if (unlikely(ret != 0))
         goto err;
 
     priv->storage.get_content = fs_storage_GetContent;
@@ -267,44 +292,46 @@ fs_storage_FromBytes(void *bytes,
     priv->destroy = fs_storage_Destroy;
 
     free(bytes);
-    return &priv->storage;
+    *out = &priv->storage;
+    return 0;
 err:
     free(bytes);
-    if (priv != NULL)
-        free(priv->fs.path);
+    free(priv->fs.path);
     free(priv);
-    return NULL;
+    return ret;
 }
 
-hls_storage_t *hls_storage_FromBlocks(block_t *content,
-                                      const struct hls_storage_config *config,
-                                      const struct hls_config *hls_config)
+int hls_storage_FromBlocks(block_t *content,
+                           const struct hls_storage_config *config,
+                           const struct hls_config *hls_config,
+                           hls_storage_t **out)
 {
-    hls_storage_t *storage;
+    int ret;
     if (hls_config_IsMemStorageEnabled(hls_config))
-        storage = mem_storage_FromBlock(content);
+        ret = mem_storage_FromBlock(content, out);
     else
-        storage = fs_storage_FromBlock(content, config, hls_config);
+        ret = fs_storage_FromBlock(content, config, hls_config, out);
 
-    if (storage != NULL)
-        storage->mime = config->mime;
-    return storage;
+    if (ret == 0)
+        (*out)->mime = config->mime;
+    return ret;
 }
 
-hls_storage_t *hls_storage_FromBytes(void *data,
-                                     size_t size,
-                                     const struct hls_storage_config *config,
-                                     const struct hls_config *hls_config)
+int hls_storage_FromBytes(void *data,
+                          size_t size,
+                          const struct hls_storage_config *config,
+                          const struct hls_config *hls_config,
+                          hls_storage_t **out)
 {
-    hls_storage_t *storage;
+    int ret;
     if (hls_config_IsMemStorageEnabled(hls_config))
-        storage = mem_storage_FromBytes(data, size);
+        ret = mem_storage_FromBytes(data, size, out);
     else
-        storage = fs_storage_FromBytes(data, size, config, hls_config);
+        ret = fs_storage_FromBytes(data, size, config, hls_config, out);
 
-    if (storage != NULL)
-        storage->mime = config->mime;
-    return storage;
+    if (ret == 0)
+        (*out)->mime = config->mime;
+    return ret;
 }
 
 size_t hls_storage_GetSize(const hls_storage_t *storage)


=====================================
modules/stream_out/hls/storage.h
=====================================
@@ -39,7 +39,7 @@ typedef struct hls_storage
      *
      * \param[out] dest Pointer on a byte buffer, will be freshly allocated by
      * the function call. \return Byte count of the byte buffer. \retval -1 On
-     * allocation error.
+     * error.
      */
     ssize_t (*get_content)(const struct hls_storage *, uint8_t **dest);
 } hls_storage_t;
@@ -52,13 +52,15 @@ typedef struct hls_storage
  * \param content The block chain.
  * \param hls_storage_config The storage specific config.
  * \param hls_config The global hls config.
+ * \param[out] out The new storage on success.
  *
- * \return An opaque pointer on the HLS storage.
- * \retval NULL on allocation error.
+ * \retval 0 on success.
+ * \retval -errno on error.
  */
-hls_storage_t *hls_storage_FromBlocks(block_t *content,
-                                      const struct hls_storage_config *,
-                                      const struct hls_config *) VLC_USED;
+int hls_storage_FromBlocks(block_t *content,
+                           const struct hls_storage_config *,
+                           const struct hls_config *,
+                           hls_storage_t **out) VLC_USED;
 
 /**
  * Create an HLS opaque storage from a byte buffer.
@@ -69,14 +71,16 @@ hls_storage_t *hls_storage_FromBlocks(block_t *content,
  * \param size Byte size of the buffer.
  * \param hls_storage_config The storage specific config.
  * \param hls_config The global hls config.
+ * \param[out] out The new storage on success.
  *
- * \return An opaque pointer on the HLS storage.
- * \retval NULL on allocation error.
+ * \retval 0 on success.
+ * \retval -errno on error.
  */
-hls_storage_t *hls_storage_FromBytes(void *data,
-                                     size_t size,
-                                     const struct hls_storage_config *,
-                                     const struct hls_config *) VLC_USED;
+int hls_storage_FromBytes(void *data,
+                          size_t size,
+                          const struct hls_storage_config *,
+                          const struct hls_config *,
+                          hls_storage_t **out) VLC_USED;
 
 size_t hls_storage_GetSize(const hls_storage_t *);
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/50e8b93f6426eb3c9baf173123969a172c2a3a0b...9e0ea06c86603467ebe4d812e5f7d1ea1af55c7b

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/50e8b93f6426eb3c9baf173123969a172c2a3a0b...9e0ea06c86603467ebe4d812e5f7d1ea1af55c7b
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list