libvirt/src/libvirt-interface.c
Erik Skultety d6064e2759 libvirt-<module>: Check caller-provided buffers to be NULL with size > 0
Pre-Glib era which used malloc allowed the size of the client-side
buffers to be declared as 0, because malloc documents that it can either
return 0 or a unique pointer on 0 size allocations.
With glib this doesn't work anymore, because glib documents that for
such allocation requests NULL is always returned which results in an
error in our public API checks server-side.
This patch complements the fix in the RPC layer by explicitly erroring
out on the following combination of args used by our legacy APIs (their
moder equivalents don't suffer from this):

function(caller-allocated-array, size, ...) {
    if (!caller-allocated-array && size > 0)
        return error;
}

treating everything else as a valid input and potentially let that fail
on the server-side rather than client-side.

https://bugzilla.redhat.com/show_bug.cgi?id=1772842

Signed-off-by: Erik Skultety <eskultet@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2019-11-21 18:16:35 +01:00

833 lines
23 KiB
C

/*
* libvirt-interface.c: entry points for virInterfacePtr APIs
*
* Copyright (C) 2006-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.1 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 <config.h>
#include "datatypes.h"
#include "virlog.h"
VIR_LOG_INIT("libvirt.interface");
#define VIR_FROM_THIS VIR_FROM_INTERFACE
/**
* virInterfaceGetConnect:
* @iface: pointer to an interface
*
* Provides the connection pointer associated with an interface. The
* reference counter on the connection is not increased by this
* call.
*
* Returns the virConnectPtr or NULL in case of failure.
*/
virConnectPtr
virInterfaceGetConnect(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, NULL);
return iface->conn;
}
/**
* virConnectListAllInterfaces:
* @conn: Pointer to the hypervisor connection.
* @ifaces: Pointer to a variable to store the array containing the interface
* objects or NULL if the list is not required (just returns number
* of interfaces).
* @flags: bitwise-OR of virConnectListAllInterfacesFlags.
*
* Collect the list of interfaces, and allocate an array to store those
* objects. This API solves the race inherent between virConnectListInterfaces
* and virConnectListDefinedInterfaces.
*
* Normally, all interfaces are returned; however, @flags can be used to
* filter the results for a smaller list of targeted interfaces. The valid
* flags are divided into groups, where each group contains bits that
* describe mutually exclusive attributes of a interface, and where all bits
* within a group describe all possible interfaces.
*
* The only group of @flags is VIR_CONNECT_LIST_INTERFACES_ACTIVE (up) and
* VIR_CONNECT_LIST_INTERFACES_INACTIVE (down) to filter the interfaces by state.
*
* Returns the number of interfaces found or -1 and sets @ifaces to NULL in case
* of error. On success, the array stored into @ifaces is guaranteed to have an
* extra allocated element set to NULL but not included in the return count,
* to make iteration easier. The caller is responsible for calling
* virStorageInterfaceFree() on each array element, then calling free() on @ifaces.
*/
int
virConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
unsigned int flags)
{
VIR_DEBUG("conn=%p, ifaces=%p, flags=0x%x", conn, ifaces, flags);
virResetLastError();
if (ifaces)
*ifaces = NULL;
virCheckConnectReturn(conn, -1);
if (conn->interfaceDriver &&
conn->interfaceDriver->connectListAllInterfaces) {
int ret;
ret = conn->interfaceDriver->connectListAllInterfaces(conn, ifaces, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virConnectNumOfInterfaces:
* @conn: pointer to the hypervisor connection
*
* Provides the number of active interfaces on the physical host.
*
* Returns the number of active interfaces found or -1 in case of error
*/
int
virConnectNumOfInterfaces(virConnectPtr conn)
{
VIR_DEBUG("conn=%p", conn);
virResetLastError();
virCheckConnectReturn(conn, -1);
if (conn->interfaceDriver && conn->interfaceDriver->connectNumOfInterfaces) {
int ret;
ret = conn->interfaceDriver->connectNumOfInterfaces(conn);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virConnectListInterfaces:
* @conn: pointer to the hypervisor connection
* @names: array to collect the list of names of interfaces
* @maxnames: size of @names
*
* Collect the list of active physical host interfaces,
* and store their names in @names
*
* For more control over the results, see virConnectListAllInterfaces().
*
* Returns the number of interfaces found or -1 in case of error. Note that
* this command is inherently racy; a interface can be started between a call
* to virConnectNumOfInterfaces() and this call; you are only guaranteed that
* all currently active interfaces were listed if the return is less than
* @maxnames. The client must call free() on each returned name.
*/
int
virConnectListInterfaces(virConnectPtr conn, char **const names, int maxnames)
{
VIR_DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames);
virResetLastError();
virCheckConnectReturn(conn, -1);
virCheckNonNullArrayArgGoto(names, maxnames, error);
virCheckNonNegativeArgGoto(maxnames, error);
if (conn->interfaceDriver && conn->interfaceDriver->connectListInterfaces) {
int ret;
ret = conn->interfaceDriver->connectListInterfaces(conn, names, maxnames);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virConnectNumOfDefinedInterfaces:
* @conn: pointer to the hypervisor connection
*
* Provides the number of defined (inactive) interfaces on the physical host.
*
* Returns the number of defined interface found or -1 in case of error
*/
int
virConnectNumOfDefinedInterfaces(virConnectPtr conn)
{
VIR_DEBUG("conn=%p", conn);
virResetLastError();
virCheckConnectReturn(conn, -1);
if (conn->interfaceDriver && conn->interfaceDriver->connectNumOfDefinedInterfaces) {
int ret;
ret = conn->interfaceDriver->connectNumOfDefinedInterfaces(conn);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virConnectListDefinedInterfaces:
* @conn: pointer to the hypervisor connection
* @names: array to collect the list of names of interfaces
* @maxnames: size of @names
*
* Collect the list of defined (inactive) physical host interfaces,
* and store their names in @names.
*
* For more control over the results, see virConnectListAllInterfaces().
*
* Returns the number of names provided in the array or -1 in case of error.
* Note that this command is inherently racy; a interface can be defined between
* a call to virConnectNumOfDefinedInterfaces() and this call; you are only
* guaranteed that all currently defined interfaces were listed if the return
* is less than @maxnames. The client must call free() on each returned name.
*/
int
virConnectListDefinedInterfaces(virConnectPtr conn,
char **const names,
int maxnames)
{
VIR_DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames);
virResetLastError();
virCheckConnectReturn(conn, -1);
virCheckNonNullArrayArgGoto(names, maxnames, error);
virCheckNonNegativeArgGoto(maxnames, error);
if (conn->interfaceDriver && conn->interfaceDriver->connectListDefinedInterfaces) {
int ret;
ret = conn->interfaceDriver->connectListDefinedInterfaces(conn, names, maxnames);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virInterfaceLookupByName:
* @conn: pointer to the hypervisor connection
* @name: name for the interface
*
* Try to lookup an interface on the given hypervisor based on its name.
*
* virInterfaceFree should be used to free the resources after the
* interface object is no longer needed.
*
* Returns a new interface object or NULL in case of failure. If the
* interface cannot be found, then VIR_ERR_NO_INTERFACE error is raised.
*/
virInterfacePtr
virInterfaceLookupByName(virConnectPtr conn, const char *name)
{
VIR_DEBUG("conn=%p, name=%s", conn, NULLSTR(name));
virResetLastError();
virCheckConnectReturn(conn, NULL);
virCheckNonNullArgGoto(name, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceLookupByName) {
virInterfacePtr ret;
ret = conn->interfaceDriver->interfaceLookupByName(conn, name);
if (!ret)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return NULL;
}
/**
* virInterfaceLookupByMACString:
* @conn: pointer to the hypervisor connection
* @macstr: the MAC for the interface (null-terminated ASCII format)
*
* Try to lookup an interface on the given hypervisor based on its MAC.
*
* virInterfaceFree should be used to free the resources after the
* interface object is no longer needed.
*
* Returns a new interface object or NULL in case of failure. If the
* interface cannot be found, then VIR_ERR_NO_INTERFACE error is raised.
*/
virInterfacePtr
virInterfaceLookupByMACString(virConnectPtr conn, const char *macstr)
{
VIR_DEBUG("conn=%p, macstr=%s", conn, NULLSTR(macstr));
virResetLastError();
virCheckConnectReturn(conn, NULL);
virCheckNonNullArgGoto(macstr, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceLookupByMACString) {
virInterfacePtr ret;
ret = conn->interfaceDriver->interfaceLookupByMACString(conn, macstr);
if (!ret)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return NULL;
}
/**
* virInterfaceGetName:
* @iface: an interface object
*
* Get the public name for that interface
*
* Returns a pointer to the name or NULL, the string need not be deallocated
* its lifetime will be the same as the interface object.
*/
const char *
virInterfaceGetName(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, NULL);
return iface->name;
}
/**
* virInterfaceGetMACString:
* @iface: an interface object
*
* Get the MAC for an interface as string. For more information about
* MAC see RFC4122.
*
* Returns a pointer to the MAC address (in null-terminated ASCII
* format) or NULL, the string need not be deallocated its lifetime
* will be the same as the interface object.
*/
const char *
virInterfaceGetMACString(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, NULL);
return iface->mac;
}
/**
* virInterfaceGetXMLDesc:
* @iface: an interface object
* @flags: bitwise-OR of extraction flags. Current valid bits:
*
* VIR_INTERFACE_XML_INACTIVE - return the static configuration,
* suitable for use redefining the
* interface via virInterfaceDefineXML()
*
* Provide an XML description of the interface. If
* VIR_INTERFACE_XML_INACTIVE is set, the description may be reused
* later to redefine the interface with virInterfaceDefineXML(). If it
* is not set, the ip address and netmask will be the current live
* setting of the interface, not the settings from the config files.
*
* Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case
* of error. The caller must free() the returned value.
*/
char *
virInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags)
{
virConnectPtr conn;
VIR_DEBUG("iface=%p, flags=0x%x", iface, flags);
virResetLastError();
virCheckInterfaceReturn(iface, NULL);
conn = iface->conn;
if (conn->interfaceDriver && conn->interfaceDriver->interfaceGetXMLDesc) {
char *ret;
ret = conn->interfaceDriver->interfaceGetXMLDesc(iface, flags);
if (!ret)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(iface->conn);
return NULL;
}
/**
* virInterfaceDefineXML:
* @conn: pointer to the hypervisor connection
* @xml: the XML description for the interface, preferably in UTF-8
* @flags: extra flags; not used yet, so callers should always pass 0
*
* Define an inactive persistent physical host interface or modify an existing
* persistent one from the XML description.
*
* Normally this change in the interface configuration is immediately
* permanent/persistent, but if virInterfaceChangeBegin() has been
* previously called (i.e. if an interface config transaction is
* open), the new interface definition will only become permanent if
* virInterfaceChangeCommit() is called prior to the next reboot of
* the system running libvirtd. Prior to that time, it can be
* explicitly removed using virInterfaceChangeRollback(), or will be
* automatically removed during the next reboot of the system running
* libvirtd.
*
* virInterfaceFree should be used to free the resources after the
* interface object is no longer needed.
*
* Returns NULL in case of error, a pointer to the interface otherwise
*/
virInterfacePtr
virInterfaceDefineXML(virConnectPtr conn, const char *xml, unsigned int flags)
{
VIR_DEBUG("conn=%p, xml=%s, flags=0x%x", conn, NULLSTR(xml), flags);
virResetLastError();
virCheckConnectReturn(conn, NULL);
virCheckReadOnlyGoto(conn->flags, error);
virCheckNonNullArgGoto(xml, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceDefineXML) {
virInterfacePtr ret;
ret = conn->interfaceDriver->interfaceDefineXML(conn, xml, flags);
if (!ret)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return NULL;
}
/**
* virInterfaceUndefine:
* @iface: pointer to a defined interface
*
* Undefine an interface, ie remove it from the config.
* This does not free the associated virInterfacePtr object.
*
* Normally this change in the interface configuration is
* permanent/persistent, but if virInterfaceChangeBegin() has been
* previously called (i.e. if an interface config transaction is
* open), the removal of the interface definition will only become
* permanent if virInterfaceChangeCommit() is called prior to the next
* reboot of the system running libvirtd. Prior to that time, the
* definition can be explicitly restored using
* virInterfaceChangeRollback(), or will be automatically restored
* during the next reboot of the system running libvirtd.
*
* Returns 0 in case of success, -1 in case of error
*/
int
virInterfaceUndefine(virInterfacePtr iface)
{
virConnectPtr conn;
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
conn = iface->conn;
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceUndefine) {
int ret;
ret = conn->interfaceDriver->interfaceUndefine(iface);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(iface->conn);
return -1;
}
/**
* virInterfaceCreate:
* @iface: pointer to a defined interface
* @flags: extra flags; not used yet, so callers should always pass 0
*
* Activate an interface (i.e. call "ifup").
*
* If there was an open network config transaction at the time this
* interface was defined (that is, if virInterfaceChangeBegin() had
* been called), the interface will be brought back down (and then
* undefined) if virInterfaceChangeRollback() is called.
*
* Returns 0 in case of success, -1 in case of error
*/
int
virInterfaceCreate(virInterfacePtr iface, unsigned int flags)
{
virConnectPtr conn;
VIR_DEBUG("iface=%p, flags=0x%x", iface, flags);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
conn = iface->conn;
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceCreate) {
int ret;
ret = conn->interfaceDriver->interfaceCreate(iface, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(iface->conn);
return -1;
}
/**
* virInterfaceDestroy:
* @iface: an interface object
* @flags: extra flags; not used yet, so callers should always pass 0
*
* deactivate an interface (ie call "ifdown")
* This does not remove the interface from the config, and
* does not free the associated virInterfacePtr object.
*
* If there is an open network config transaction at the time this
* interface is destroyed (that is, if virInterfaceChangeBegin() had
* been called), and if the interface is later undefined and then
* virInterfaceChangeRollback() is called, the restoral of the
* interface definition will also bring the interface back up.
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
virInterfaceDestroy(virInterfacePtr iface, unsigned int flags)
{
virConnectPtr conn;
VIR_DEBUG("iface=%p, flags=0x%x", iface, flags);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
conn = iface->conn;
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceDestroy) {
int ret;
ret = conn->interfaceDriver->interfaceDestroy(iface, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(iface->conn);
return -1;
}
/**
* virInterfaceRef:
* @iface: the interface to hold a reference on
*
* Increment the reference count on the interface. For each
* additional call to this method, there shall be a corresponding
* call to virInterfaceFree to release the reference count, once
* the caller no longer needs the reference to this object.
*
* This method is typically useful for applications where multiple
* threads are using a connection, and it is required that the
* connection remain open until all threads have finished using
* it. ie, each new thread using an interface would increment
* the reference count.
*
* Returns 0 in case of success, -1 in case of failure.
*/
int
virInterfaceRef(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p refs=%d", iface, iface ? iface->parent.u.s.refs : 0);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
virObjectRef(iface);
return 0;
}
/**
* virInterfaceFree:
* @iface: an interface object
*
* Free the interface object. The interface itself is unaltered.
* The data structure is freed and should not be used thereafter.
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
virInterfaceFree(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
virObjectUnref(iface);
return 0;
}
/**
* virInterfaceChangeBegin:
* @conn: pointer to hypervisor connection
* @flags: extra flags; not used yet, so callers should always pass 0
*
* This function creates a restore point to which one can return
* later by calling virInterfaceChangeRollback(). This function should
* be called before any transaction with interface configuration.
* Once it is known that a new configuration works, it can be committed via
* virInterfaceChangeCommit(), which frees the restore point.
*
* If virInterfaceChangeBegin() is called when a transaction is
* already opened, this function will fail, and a
* VIR_ERR_INVALID_OPERATION will be logged.
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
virInterfaceChangeBegin(virConnectPtr conn, unsigned int flags)
{
VIR_DEBUG("conn=%p, flags=0x%x", conn, flags);
virResetLastError();
virCheckConnectReturn(conn, -1);
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeBegin) {
int ret;
ret = conn->interfaceDriver->interfaceChangeBegin(conn, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virInterfaceChangeCommit:
* @conn: pointer to hypervisor connection
* @flags: extra flags; not used yet, so callers should always pass 0
*
* This commits the changes made to interfaces and frees the restore point
* created by virInterfaceChangeBegin().
*
* If virInterfaceChangeCommit() is called when a transaction is not
* opened, this function will fail, and a VIR_ERR_INVALID_OPERATION
* will be logged.
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
virInterfaceChangeCommit(virConnectPtr conn, unsigned int flags)
{
VIR_DEBUG("conn=%p, flags=0x%x", conn, flags);
virResetLastError();
virCheckConnectReturn(conn, -1);
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeCommit) {
int ret;
ret = conn->interfaceDriver->interfaceChangeCommit(conn, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virInterfaceChangeRollback:
* @conn: pointer to hypervisor connection
* @flags: extra flags; not used yet, so callers should always pass 0
*
* This cancels changes made to interfaces settings by restoring previous
* state created by virInterfaceChangeBegin().
*
* If virInterfaceChangeRollback() is called when a transaction is not
* opened, this function will fail, and a VIR_ERR_INVALID_OPERATION
* will be logged.
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
virInterfaceChangeRollback(virConnectPtr conn, unsigned int flags)
{
VIR_DEBUG("conn=%p, flags=0x%x", conn, flags);
virResetLastError();
virCheckConnectReturn(conn, -1);
virCheckReadOnlyGoto(conn->flags, error);
if (conn->interfaceDriver &&
conn->interfaceDriver->interfaceChangeRollback) {
int ret;
ret = conn->interfaceDriver->interfaceChangeRollback(conn, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virInterfaceIsActive:
* @iface: pointer to the interface object
*
* Determine if the interface is currently running
*
* Returns 1 if running, 0 if inactive, -1 on error
*/
int
virInterfaceIsActive(virInterfacePtr iface)
{
VIR_DEBUG("iface=%p", iface);
virResetLastError();
virCheckInterfaceReturn(iface, -1);
if (iface->conn->interfaceDriver->interfaceIsActive) {
int ret;
ret = iface->conn->interfaceDriver->interfaceIsActive(iface);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(iface->conn);
return -1;
}