...
 
Commits (29)
......@@ -4,6 +4,7 @@ variables:
DEPS: meson ninja-build make
gcc pkg-config glib2-devel
mingw64-gcc mingw64-pkg-config mingw64-glib2
clang-analyzer
before_script:
- dnf install -y $DEPS
......@@ -13,6 +14,7 @@ build:
- meson build || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1)
- ninja -C build scan-build
build-mingw64:
script:
......
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.1.0] - 2019-12-02
### Added
- The `slirp_new()` API, simpler and more extensible than `slirp_init()`.
- Allow custom MTU configuration.
- Option to disable host loopback connections.
- CI now runs scan-build too.
### Changed
- Disable `tcp_emu()` by default. `tcp_emu()` is known to have caused
several CVEs, and not useful today in most cases. The feature can
be still enabled by setting `SlirpConfig.enable_emu` to true.
- meson build system is now `subproject()` friendly.
- Replace remaining `malloc()`/`free()` with glib (which aborts on OOM)
- Various code cleanups.
### Deprecated
- The `slirp_init()` API.
### Fixed
- `getpeername()` error after `shutdown(SHUT_WR)`.
- Exec forward: correctly parse command lines that contain spaces.
- Allow 0.0.0.0 destination address.
- Make host receive broadcast packets.
- Various memory related fixes (heap overflow, leaks, NULL
dereference).
- Compilation warnings, dead code.
## [4.0.0] - 2019-05-24
### Added
- Installable as a shared library.
- meson build system
(& make build system for in-tree QEMU integration)
### Changed
- Standalone project, removing any QEMU dependency.
- License clarifications.
[unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.1.0...master
[4.1.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.0.0...v4.1.0
[4.0.0]: https://gitlab.freedesktop.org/slirp/libslirp/commits/v4.0.0
......@@ -3,7 +3,7 @@ BUILD_DIR ?= .
LIBSLIRP = $(BUILD_DIR)/libslirp.a
SLIRP_MAJOR_VERSION = 4
SLIRP_MINOR_VERSION = 0
SLIRP_MINOR_VERSION = 1
SLIRP_MICRO_VERSION = 0
all: $(LIBSLIRP)
......
project('libslirp', 'c',
version : '4.0.0',
version : '4.1.0',
license : 'BSD-3-Clause',
default_options : ['warning_level=1', 'c_std=gnu99']
)
......@@ -32,10 +32,10 @@ conf.set('SLIRP_MICRO_VERSION', micro_version)
# - If the interface is the same as the previous version, but bugs are
# fixed, change:
# REVISION += 1
lt_current = '0'
lt_revision = '0'
lt_age = '0'
lt_version = '@0@.@1@.@2@'.format(lt_current, lt_age, lt_revision)
lt_current = 1
lt_revision = 0
lt_age = 1
lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision)
host_system = host_machine.system()
......@@ -100,8 +100,7 @@ configure_file(
configuration : conf
)
lib = shared_library('slirp', sources,
soversion : lt_current,
lib = library('slirp', sources,
version : lt_version,
c_args : cargs,
link_args : vflag,
......@@ -110,6 +109,10 @@ lib = shared_library('slirp', sources,
install : true
)
libslirp_dep = declare_dependency(
include_directories: include_directories('.', 'src'),
link_with: lib)
install_headers(['src/libslirp.h'], subdir : 'slirp')
pkg = import('pkgconfig')
......
......@@ -71,7 +71,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr }));
/* If broadcast address */
if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) {
/* return Ethernet broadcast address */
memset(out_ethaddr, 0xff, ETH_ALEN);
return 1;
......
......@@ -238,13 +238,8 @@ int translate_dnssearch(Slirp *s, const char **names)
size_t i, num_domains, memreq = 0;
uint8_t *result = NULL, *outptr;
CompactDomain *domains = NULL;
const char **nameptr = names;
while (*nameptr != NULL) {
nameptr++;
}
num_domains = nameptr - names;
num_domains = g_strv_length((GStrv)names);
if (num_domains == 0) {
return -2;
}
......
......@@ -7,7 +7,6 @@ global:
slirp_connection_info;
slirp_init;
slirp_input;
slirp_new;
slirp_pollfds_fill;
slirp_pollfds_poll;
slirp_remove_hostfwd;
......@@ -20,3 +19,7 @@ global:
local:
*;
};
SLIRP_4.1 {
slirp_new;
} SLIRP_4.0;
......@@ -168,7 +168,8 @@ g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv,
int fork_exec(struct socket *so, const char *ex)
{
GError *err = NULL;
char **argv;
gint argc = 0;
gchar **argv = NULL;
int opt, sp[2];
DEBUG_CALL("fork_exec");
......@@ -179,7 +180,12 @@ int fork_exec(struct socket *so, const char *ex)
return 0;
}
argv = g_strsplit(ex, " ", -1);
if (!g_shell_parse_argv(ex, &argc, &argv, &err)) {
g_critical("fork_exec invalid command: %s\nerror: %s", ex, err->message);
g_error_free(err);
return 0;
}
g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */,
G_SPAWN_SEARCH_PATH, fork_exec_child_setup,
NULL /* data */, NULL /* child_pid */, sp[1], sp[1],
......
......@@ -9,19 +9,17 @@ static void sbappendsb(struct sbuf *sb, struct mbuf *m);
void sbfree(struct sbuf *sb)
{
free(sb->sb_data);
g_free(sb->sb_data);
}
bool sbdrop(struct sbuf *sb, int num)
bool sbdrop(struct sbuf *sb, size_t num)
{
int limit = sb->sb_datalen / 2;
/*
* We can only drop how much we have
* This should never succeed
*/
g_warn_if_fail(num <= sb->sb_cc);
if (num > sb->sb_cc)
num = sb->sb_cc;
sb->sb_cc -= num;
sb->sb_rptr += num;
if (sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
......@@ -34,27 +32,11 @@ bool sbdrop(struct sbuf *sb, int num)
return false;
}
void sbreserve(struct sbuf *sb, int size)
void sbreserve(struct sbuf *sb, size_t size)
{
if (sb->sb_data) {
/* Already alloced, realloc if necessary */
if (sb->sb_datalen != size) {
sb->sb_wptr = sb->sb_rptr = sb->sb_data =
(char *)realloc(sb->sb_data, size);
sb->sb_cc = 0;
if (sb->sb_wptr)
sb->sb_datalen = size;
else
sb->sb_datalen = 0;
}
} else {
sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
sb->sb_cc = 0;
if (sb->sb_wptr)
sb->sb_datalen = size;
else
sb->sb_datalen = 0;
}
sb->sb_wptr = sb->sb_rptr = sb->sb_data = g_realloc(sb->sb_data, size);
sb->sb_cc = 0;
sb->sb_datalen = size;
}
/*
......@@ -161,17 +143,17 @@ static void sbappendsb(struct sbuf *sb, struct mbuf *m)
* Don't update the sbuf rptr, this will be
* done in sbdrop when the data is acked
*/
void sbcopy(struct sbuf *sb, int off, int len, char *to)
void sbcopy(struct sbuf *sb, size_t off, size_t len, char *to)
{
char *from;
g_assert(len + off <= sb->sb_cc);
from = sb->sb_rptr + off;
if (from >= sb->sb_data + sb->sb_datalen)
from -= sb->sb_datalen;
if (from < sb->sb_wptr) {
if (len > sb->sb_cc)
len = sb->sb_cc;
memcpy(to, from, len);
} else {
/* re-use off */
......
......@@ -18,10 +18,10 @@ struct sbuf {
char *sb_data; /* Actual data */
};
void sbfree(struct sbuf *);
bool sbdrop(struct sbuf *, int);
void sbreserve(struct sbuf *, int);
void sbappend(struct socket *, struct mbuf *);
void sbcopy(struct sbuf *, int, int, char *);
void sbfree(struct sbuf *sb);
bool sbdrop(struct sbuf *sb, size_t len);
void sbreserve(struct sbuf *sb, size_t size);
void sbappend(struct socket *sb, struct mbuf *mb);
void sbcopy(struct sbuf *sb, size_t off, size_t len, char *p);
#endif
......@@ -845,11 +845,6 @@ static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
{
const struct ip *iph = (const struct ip *)ifm->m_data;
if (iph->ip_dst.s_addr == 0) {
/* 0.0.0.0 can not be a destination address, something went wrong,
* avoid making it worse */
return 1;
}
if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) {
uint8_t arp_req[ETH_HLEN + sizeof(struct slirp_arphdr)];
struct ethhdr *reh = (struct ethhdr *)arp_req;
......
......@@ -266,7 +266,7 @@ struct tcpcb *tcp_close(register struct tcpcb *);
void tcp_sockclosed(struct tcpcb *);
int tcp_fconnect(struct socket *, unsigned short af);
void tcp_connect(struct socket *);
int tcp_attach(struct socket *);
void tcp_attach(struct socket *);
uint8_t tcp_tos(struct socket *);
int tcp_emu(struct socket *, struct mbuf *);
int tcp_ctl(struct socket *);
......
......@@ -95,7 +95,7 @@ void sofree(struct socket *so)
remque(so); /* crashes if so is not in a queue */
if (so->so_tcpcb) {
free(so->so_tcpcb);
g_free(so->so_tcpcb);
}
g_free(so);
}
......@@ -195,7 +195,9 @@ int soread(struct socket *so)
err = errno;
if (nn == 0) {
if (getpeername(so->s, paddr, &alen) < 0) {
int shutdown_wr = so->so_state & SS_FCANTSENDMORE;
if (!shutdown_wr && getpeername(so->s, paddr, &alen) < 0) {
err = errno;
} else {
getsockopt(so->s, SOL_SOCKET, SO_ERROR, &err, &elen);
......@@ -844,6 +846,9 @@ int sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
} else {
sin->sin_addr = loopback_addr;
}
} else if (!slirp->disable_host_loopback && so->so_faddr.s_addr == 0xffffffff) {
/* Receive broadcast as well */
sin->sin_addr = loopback_addr;
}
break;
case AF_INET6:
......@@ -863,6 +868,9 @@ int sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
} else {
sin6->sin6_addr = in6addr_loopback;
}
} else if (!slirp->disable_host_loopback
&& in6_equal(&so->so_faddr6, &(struct in6_addr) ALLNODES_MULTICAST)) {
sin6->sin6_addr = in6addr_loopback;
}
break;
......
......@@ -107,9 +107,6 @@ static int sbuf_tmp_post_load(void *opaque, int version)
/* Allocate the buffer space used by the field after the tmp */
sbreserve(tmp->parent, tmp->parent->sb_datalen);
if (tmp->parent->sb_datalen != requested_len) {
return -ENOMEM;
}
if (tmp->woff >= requested_len || tmp->roff >= requested_len) {
g_critical("invalid sbuf offsets r/w=%u/%u len=%u", tmp->roff,
tmp->woff, requested_len);
......@@ -159,9 +156,8 @@ static bool slirp_family_inet(void *opaque, int version_id)
static int slirp_socket_pre_load(void *opaque)
{
struct socket *so = opaque;
if (tcp_attach(so) < 0) {
return -ENOMEM;
}
tcp_attach(so);
/* Older versions don't load these fields */
so->so_ffamily = AF_INET;
so->so_lfamily = AF_INET;
......
......@@ -408,10 +408,7 @@ findso:
goto dropwithreset;
so = socreate(slirp);
if (tcp_attach(so) < 0) {
g_free(so); /* Not sofree (if it failed, it's not insqued) */
goto dropwithreset;
}
tcp_attach(so);
sbreserve(&so->so_snd, TCP_SNDSPACE);
sbreserve(&so->so_rcv, TCP_RCVSPACE);
......
......@@ -255,11 +255,7 @@ struct tcpcb *tcp_newtcpcb(struct socket *so)
{
register struct tcpcb *tp;
tp = (struct tcpcb *)malloc(sizeof(*tp));
if (tp == NULL)
return ((struct tcpcb *)0);
memset((char *)tp, 0, sizeof(struct tcpcb));
tp = g_new0(struct tcpcb, 1);
tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp;
/*
* 40: length of IPv4 header (20) + TCP header (20)
......@@ -336,7 +332,7 @@ struct tcpcb *tcp_close(struct tcpcb *tp)
remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
m_free(m);
}
free(tp);
g_free(tp);
so->so_tcpcb = NULL;
/* clobber input socket cache if we're closing the cached connection */
if (so == slirp->tcp_last_so)
......@@ -377,8 +373,8 @@ void tcp_sockclosed(struct tcpcb *tp)
case TCPS_LISTEN:
case TCPS_SYN_SENT:
tp->t_state = TCPS_CLOSED;
tp = tcp_close(tp);
break;
tcp_close(tp);
return;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
......@@ -474,10 +470,7 @@ void tcp_connect(struct socket *inso)
so = inso;
} else {
so = socreate(slirp);
if (tcp_attach(so) < 0) {
g_free(so); /* NOT sofree */
return;
}
tcp_attach(so);
so->lhost = inso->lhost;
so->so_ffamily = inso->so_ffamily;
}
......@@ -528,14 +521,10 @@ void tcp_connect(struct socket *inso)
/*
* Attach a TCPCB to a socket.
*/
int tcp_attach(struct socket *so)
void tcp_attach(struct socket *so)
{
if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
return -1;
so->so_tcpcb = tcp_newtcpcb(so);
insque(so, &so->slirp->tcb);
return 0;
}
/*
......
......@@ -321,7 +321,6 @@ static int vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd,
}
for (i = 0; i < n_elems; i++) {
void *curr_elem = first_elem + size * i;
ret = 0;
if (field->flags & VMS_ARRAY_OF_POINTER) {
assert(curr_elem);
......