[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