qemu/io/channel-buffer.c
Daniel P. Berrange d656ec5ea8 io: avoid double-free when closing QIOChannelBuffer
The QIOChannelBuffer's close implementation will free
the internal data buffer. It failed to reset the pointer
to NULL though, so when the object is later finalized
it will free it a second time with predictable crash.

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <1461751518-12128-3-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-05-26 11:31:09 +05:30

251 lines
6.9 KiB
C

/*
* QEMU I/O channels memory buffer driver
*
* Copyright (c) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "io/channel-buffer.h"
#include "io/channel-watch.h"
#include "qemu/sockets.h"
#include "trace.h"
QIOChannelBuffer *
qio_channel_buffer_new(size_t capacity)
{
QIOChannelBuffer *ioc;
ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER));
if (capacity) {
ioc->data = g_new0(uint8_t, capacity);
ioc->capacity = capacity;
}
return ioc;
}
static void qio_channel_buffer_finalize(Object *obj)
{
QIOChannelBuffer *ioc = QIO_CHANNEL_BUFFER(obj);
g_free(ioc->data);
ioc->capacity = ioc->usage = ioc->offset = 0;
}
static ssize_t qio_channel_buffer_readv(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int **fds,
size_t *nfds,
Error **errp)
{
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
ssize_t ret = 0;
size_t i;
for (i = 0; i < niov; i++) {
size_t want = iov[i].iov_len;
if (bioc->offset >= bioc->usage) {
break;
}
if ((bioc->offset + want) > bioc->usage) {
want = bioc->usage - bioc->offset;
}
memcpy(iov[i].iov_base, bioc->data + bioc->offset, want);
ret += want;
bioc->offset += want;
}
return ret;
}
static ssize_t qio_channel_buffer_writev(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
int *fds,
size_t nfds,
Error **errp)
{
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
ssize_t ret = 0;
size_t i;
size_t towrite = 0;
for (i = 0; i < niov; i++) {
towrite += iov[i].iov_len;
}
if ((bioc->offset + towrite) > bioc->capacity) {
bioc->capacity = bioc->offset + towrite;
bioc->data = g_realloc(bioc->data, bioc->capacity);
}
if (bioc->offset > bioc->usage) {
memset(bioc->data, 0, bioc->offset - bioc->usage);
bioc->usage = bioc->offset;
}
for (i = 0; i < niov; i++) {
memcpy(bioc->data + bioc->usage,
iov[i].iov_base,
iov[i].iov_len);
bioc->usage += iov[i].iov_len;
bioc->offset += iov[i].iov_len;
ret += iov[i].iov_len;
}
return ret;
}
static int qio_channel_buffer_set_blocking(QIOChannel *ioc G_GNUC_UNUSED,
bool enabled G_GNUC_UNUSED,
Error **errp G_GNUC_UNUSED)
{
return 0;
}
static off_t qio_channel_buffer_seek(QIOChannel *ioc,
off_t offset,
int whence,
Error **errp)
{
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
bioc->offset = offset;
return offset;
}
static int qio_channel_buffer_close(QIOChannel *ioc,
Error **errp)
{
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
g_free(bioc->data);
bioc->data = NULL;
bioc->capacity = bioc->usage = bioc->offset = 0;
return 0;
}
typedef struct QIOChannelBufferSource QIOChannelBufferSource;
struct QIOChannelBufferSource {
GSource parent;
QIOChannelBuffer *bioc;
GIOCondition condition;
};
static gboolean
qio_channel_buffer_source_prepare(GSource *source,
gint *timeout)
{
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
*timeout = -1;
return (G_IO_IN | G_IO_OUT) & bsource->condition;
}
static gboolean
qio_channel_buffer_source_check(GSource *source)
{
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
return (G_IO_IN | G_IO_OUT) & bsource->condition;
}
static gboolean
qio_channel_buffer_source_dispatch(GSource *source,
GSourceFunc callback,
gpointer user_data)
{
QIOChannelFunc func = (QIOChannelFunc)callback;
QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
return (*func)(QIO_CHANNEL(bsource->bioc),
((G_IO_IN | G_IO_OUT) & bsource->condition),
user_data);
}
static void
qio_channel_buffer_source_finalize(GSource *source)
{
QIOChannelBufferSource *ssource = (QIOChannelBufferSource *)source;
object_unref(OBJECT(ssource->bioc));
}
GSourceFuncs qio_channel_buffer_source_funcs = {
qio_channel_buffer_source_prepare,
qio_channel_buffer_source_check,
qio_channel_buffer_source_dispatch,
qio_channel_buffer_source_finalize
};
static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc,
GIOCondition condition)
{
QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
QIOChannelBufferSource *ssource;
GSource *source;
source = g_source_new(&qio_channel_buffer_source_funcs,
sizeof(QIOChannelBufferSource));
ssource = (QIOChannelBufferSource *)source;
ssource->bioc = bioc;
object_ref(OBJECT(bioc));
ssource->condition = condition;
return source;
}
static void qio_channel_buffer_class_init(ObjectClass *klass,
void *class_data G_GNUC_UNUSED)
{
QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
ioc_klass->io_writev = qio_channel_buffer_writev;
ioc_klass->io_readv = qio_channel_buffer_readv;
ioc_klass->io_set_blocking = qio_channel_buffer_set_blocking;
ioc_klass->io_seek = qio_channel_buffer_seek;
ioc_klass->io_close = qio_channel_buffer_close;
ioc_klass->io_create_watch = qio_channel_buffer_create_watch;
}
static const TypeInfo qio_channel_buffer_info = {
.parent = TYPE_QIO_CHANNEL,
.name = TYPE_QIO_CHANNEL_BUFFER,
.instance_size = sizeof(QIOChannelBuffer),
.instance_finalize = qio_channel_buffer_finalize,
.class_init = qio_channel_buffer_class_init,
};
static void qio_channel_buffer_register_types(void)
{
type_register_static(&qio_channel_buffer_info);
}
type_init(qio_channel_buffer_register_types);