summaryrefslogtreecommitdiffstats
path: root/server/snd_worker.c
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-08-23 14:14:22 +0300
committerAlon Levy <alevy@redhat.com>2011-08-23 18:30:26 +0300
commit0b169b7014d657da6a64848168db7136ecbb191f (patch)
treedd62e290c59cfcd1ed7d9814278bff1ab24ba899 /server/snd_worker.c
parent1078dc04edc406950e5f6d91bae456411eaa4a47 (diff)
downloadspice-0b169b7014d657da6a64848168db7136ecbb191f.tar.gz
spice-0b169b7014d657da6a64848168db7136ecbb191f.tar.xz
spice-0b169b7014d657da6a64848168db7136ecbb191f.zip
server/snd_worker.c: add reference counting to SndChannel
Fixes a valgrind discovered possible bug in spice-server - valgrind on test_playback saw it, didn't see it happen with qemu. The problem is that the frames buffers returned by spice_server_playback_get_buffer are part of the malloc'ed SndChannel, whose lifetime is smaller then that of SndWorker. As a result a pointer to a previously returned spice_server_playback_get_buffer could remain and be used after SndChannel has been freed because the client disconnected.
Diffstat (limited to 'server/snd_worker.c')
-rw-r--r--server/snd_worker.c31
1 files changed, 28 insertions, 3 deletions
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 08947859..98f3cd12 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -84,6 +84,7 @@ struct SndChannel {
RedsStream *stream;
SndWorker *worker;
spice_parse_channel_func_t parser;
+ int refs;
RedChannelClient *channel_client;
@@ -207,6 +208,23 @@ static int check_cap(uint32_t *caps, int num_caps, uint32_t cap)
return caps[i] & cap;
}
+static SndChannel *snd_channel_get(SndChannel *channel)
+{
+ channel->refs++;
+ return channel;
+}
+
+static SndChannel *snd_channel_put(SndChannel *channel)
+{
+ if (!--channel->refs) {
+ channel->worker->connection = NULL;
+ free(channel);
+ red_printf("sound channel freed\n");
+ return NULL;
+ }
+ return channel;
+}
+
static void snd_disconnect_channel(SndChannel *channel)
{
SndWorker *worker;
@@ -217,13 +235,12 @@ static void snd_disconnect_channel(SndChannel *channel)
channel->cleanup(channel);
worker = channel->worker;
red_channel_client_destroy_dummy(worker->connection->channel_client);
- worker->connection = NULL;
core->watch_remove(channel->stream->watch);
channel->stream->watch = NULL;
reds_stream_free(channel->stream);
spice_marshaller_destroy(channel->send_data.marshaller);
free(channel->caps);
- free(channel);
+ snd_channel_put(channel);
}
static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
@@ -903,6 +920,7 @@ static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_i
ASSERT(size >= sizeof(*channel));
channel = spice_malloc0(size);
+ channel->refs = 1;
channel->parser = spice_get_client_channel_parser(channel_id, NULL);
channel->stream = stream;
channel->worker = worker;
@@ -949,6 +967,7 @@ static void snd_disconnect_channel_client(RedChannelClient *rcc)
ASSERT(worker->connection->channel_client == rcc);
snd_disconnect_channel(worker->connection);
+ ASSERT(worker->connection == NULL);
}
static void snd_set_command(SndChannel *channel, uint32_t command)
@@ -1049,6 +1068,7 @@ SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *
return;
}
ASSERT(playback_channel->base.active);
+ snd_channel_get(channel);
*frame = playback_channel->free_frames->samples;
playback_channel->free_frames = playback_channel->free_frames->next;
@@ -1061,8 +1081,13 @@ SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance
PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
AudioFrame *frame;
- if (!channel)
+ if (!channel) {
return;
+ }
+ if (!snd_channel_put(channel)) {
+ /* lost last reference, channel has been destroyed previously */
+ return;
+ }
ASSERT(playback_channel->base.active);
if (playback_channel->pending_frame) {