aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml33
-rw-r--r--CHANGELOG.md22
-rw-r--r--meson.build9
-rw-r--r--src/bootp.c36
-rw-r--r--src/bootp.h2
-rw-r--r--src/libslirp.h3
-rw-r--r--src/mbuf.c16
-rw-r--r--src/mbuf.h2
-rw-r--r--src/misc.c2
-rw-r--r--src/slirp.c37
-rw-r--r--src/slirp.h2
-rw-r--r--src/tcp_input.c3
-rw-r--r--src/tcp_subr.c16
-rw-r--r--src/tftp.c62
-rw-r--r--src/tftp.h6
-rw-r--r--src/udp.c5
-rw-r--r--src/udp6.c5
17 files changed, 188 insertions, 73 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 46e58a9..2512ef1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -41,3 +41,36 @@ Coverity:
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@cov-int.tar.gz --form version="`git describe --tags`"
--form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID "
+
+integration-slirp4netns:
+ variables:
+ SLIRP4NETNS_VERSION: "v1.1.12"
+ # Consumed by `make benchmark`
+ BENCHMARK_IPERF3_DURATION: "10"
+ script:
+ # Install libslirp
+ - meson build
+ - ninja -C build install
+ # Register the path of libslirp.so.0
+ - echo /usr/local/lib64 >/etc/ld.so.conf.d/libslirp.conf
+ - ldconfig
+ # Install the dependencies of slirp4netns and its test suite
+ # TODO: install udhcpc for `slirp4netns/tests/test-slirp4netns-dhcp.sh` (currently skipped, due to lack of udhcpc)
+ - dnf install -y autoconf automake findutils iperf3 iproute iputils jq libcap-devel libseccomp-devel nmap-ncat util-linux
+ # Check whether the runner environment is configured correctly
+ - unshare -rn true || (echo Make sure you have relaxed seccomp and appamor && exit 1)
+ - unshare -rn ip tap add tap0 mode tap || (echo Make sure you have /dev/net/tun && exit 1)
+ # Install slirp4netns
+ - git clone https://github.com/rootless-containers/slirp4netns -b "${SLIRP4NETNS_VERSION}"
+ - cd slirp4netns
+ - ./autogen.sh
+ - ./configure
+ - make
+ - make install
+ - slirp4netns --version
+ # Run slirp4netns integration test
+ - make distcheck || (cat $(find . -name 'test-suite.log' ) && exit 1)
+ # Run benchmark test to ensure that libslirp can actually handle packets, with several MTU configurations
+ - make benchmark MTU=1500
+ - make benchmark MTU=512
+ - make benchmark MTU=65520
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f33557..bd4845c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,16 +5,30 @@ 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).
-## [Unreleased]
+## [4.6.1] - 2021-06-18
+
+### Fixed
+
+ - Fix DHCP regression introduced in 4.6.0. !95
+
+## [4.6.0] - 2021-06-14
### Added
+ - mbuf: Add debugging helpers for allocation. !90
+
### Changed
-### Deprecated
+ - Revert "Set macOS deployment target to macOS 10.4". !93
### Fixed
+ - mtod()-related buffer overflows (CVE-2021-3592 #44, CVE-2021-3593 #45,
+ CVE-2021-3594 #47, CVE-2021-3595 #46).
+ - poll_fd: add missing fd registration for UDP and ICMP
+ - ncsi: make ncsi_calculate_checksum work with unaligned data. !89
+ - Various typos and doc fixes. !88
+
## [4.5.0] - 2021-05-18
### Added
@@ -158,7 +172,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Standalone project, removing any QEMU dependency.
- License clarifications.
-[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...master
+[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...master
+[4.6.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.0...v4.6.1
+[4.6.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...v4.6.0
[4.5.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.4.0...v4.5.0
[4.4.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.1...v4.4.0
[4.3.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.0...v4.3.1
diff --git a/meson.build b/meson.build
index 4407a77..cb1396a 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('libslirp', 'c',
- version : '4.5.0',
+ version : '4.6.1',
license : 'BSD-3-Clause',
default_options : ['warning_level=1', 'c_std=gnu99'],
meson_version : '>= 0.50',
@@ -45,7 +45,7 @@ conf.set_quoted('SLIRP_VERSION_STRING', full_version + get_option('version_suffi
# fixed, change:
# REVISION += 1
lt_current = 3
-lt_revision = 0
+lt_revision = 1
lt_age = 3
lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision)
@@ -117,11 +117,6 @@ if cc.has_link_argument(vflag_test)
vflag += vflag_test
endif
-if host_system == 'darwin'
- cargs += '-mmacosx-version-min=10.4'
- vflag += '-mmacosx-version-min=10.4'
-endif
-
install_devel = not meson.is_subproject()
configure_file(
diff --git a/src/bootp.c b/src/bootp.c
index 46e9681..d78d61b 100644
--- a/src/bootp.c
+++ b/src/bootp.c
@@ -92,21 +92,22 @@ found:
return bc;
}
-static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+static void dhcp_decode(const struct bootp_t *bp,
+ const uint8_t *bp_end,
+ int *pmsg_type,
struct in_addr *preq_addr)
{
- const uint8_t *p, *p_end;
+ const uint8_t *p;
int len, tag;
*pmsg_type = 0;
preq_addr->s_addr = htonl(0L);
p = bp->bp_vend;
- p_end = p + DHCP_OPT_LEN;
if (memcmp(p, rfc1533_cookie, 4) != 0)
return;
p += 4;
- while (p < p_end) {
+ while (p < bp_end) {
tag = p[0];
if (tag == RFC1533_PAD) {
p++;
@@ -114,10 +115,10 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
break;
} else {
p++;
- if (p >= p_end)
+ if (p >= bp_end)
break;
len = *p++;
- if (p + len > p_end) {
+ if (p + len > bp_end) {
break;
}
DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
@@ -144,7 +145,9 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
}
}
-static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
+static void bootp_reply(Slirp *slirp,
+ const struct bootp_t *bp,
+ const uint8_t *bp_end)
{
BOOTPClient *bc = NULL;
struct mbuf *m;
@@ -157,7 +160,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
uint8_t client_ethaddr[ETH_ALEN];
/* extract exact DHCP msg type */
- dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+ dhcp_decode(bp, bp_end, &dhcp_msg_type, &preq_addr);
DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
if (preq_addr.s_addr != htonl(0L))
DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr));
@@ -179,9 +182,10 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
return;
}
m->m_data += IF_MAXLINKHDR;
+ m_inc(m, sizeof(struct bootp_t) + DHCP_OPT_LEN);
rbp = (struct bootp_t *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
- memset(rbp, 0, sizeof(struct bootp_t));
+ memset(rbp, 0, sizeof(struct bootp_t) + DHCP_OPT_LEN);
if (dhcp_msg_type == DHCPDISCOVER) {
if (preq_addr.s_addr != htonl(0L)) {
@@ -235,7 +239,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
q = rbp->bp_vend;
- end = (uint8_t *)&rbp[1];
+ end = rbp->bp_vend + DHCP_OPT_LEN;
memcpy(q, rfc1533_cookie, 4);
q += 4;
@@ -351,19 +355,21 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
q += sizeof(nak_msg) - 1;
}
assert(q < end);
- *q = RFC1533_END;
+ *q++ = RFC1533_END;
daddr.sin_addr.s_addr = 0xffffffffu;
- m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr);
+ assert(q <= end);
+
+ m->m_len = sizeof(struct bootp_t) + (end - rbp->bp_vend) - sizeof(struct ip) - sizeof(struct udphdr);
udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
}
void bootp_input(struct mbuf *m)
{
- struct bootp_t *bp = mtod(m, struct bootp_t *);
+ struct bootp_t *bp = mtod_check(m, sizeof(struct bootp_t));
- if (bp->bp_op == BOOTP_REQUEST) {
- bootp_reply(m->slirp, bp);
+ if (bp && bp->bp_op == BOOTP_REQUEST) {
+ bootp_reply(m->slirp, bp, m_end(m));
}
}
diff --git a/src/bootp.h b/src/bootp.h
index a57fa51..31ce5fd 100644
--- a/src/bootp.h
+++ b/src/bootp.h
@@ -114,7 +114,7 @@ struct bootp_t {
uint8_t bp_hwaddr[16];
uint8_t bp_sname[64];
char bp_file[128];
- uint8_t bp_vend[DHCP_OPT_LEN];
+ uint8_t bp_vend[];
};
typedef struct {
diff --git a/src/libslirp.h b/src/libslirp.h
index 5760d53..8e4ea33 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -68,7 +68,8 @@ typedef struct SlirpCb {
void (*register_poll_fd)(int fd, void *opaque);
/* Unregister a fd */
void (*unregister_poll_fd)(int fd, void *opaque);
- /* Kick the io-thread, to signal that new events may be processed */
+ /* Kick the io-thread, to signal that new events may be processed because some TCP buffer
+ * can now receive more data, i.e. slirp_socket_can_recv will return 1. */
void (*notify)(void *opaque);
} SlirpCb;
diff --git a/src/mbuf.c b/src/mbuf.c
index b47f64e..36864a4 100644
--- a/src/mbuf.c
+++ b/src/mbuf.c
@@ -263,3 +263,19 @@ struct mbuf *m_dup(Slirp *slirp, struct mbuf *m,
return n;
}
+
+void *mtod_check(struct mbuf *m, size_t len)
+{
+ if (m->m_len >= len) {
+ return m->m_data;
+ }
+
+ DEBUG_ERROR("mtod failed");
+
+ return NULL;
+}
+
+void *m_end(struct mbuf *m)
+{
+ return m->m_data + m->m_len;
+}
diff --git a/src/mbuf.h b/src/mbuf.h
index 47b3b14..34e697a 100644
--- a/src/mbuf.h
+++ b/src/mbuf.h
@@ -126,6 +126,8 @@ void m_adj(struct mbuf *, int);
int m_copy(struct mbuf *, struct mbuf *, int, int);
struct mbuf *m_dup(Slirp *slirp, struct mbuf *m, bool copy_header, size_t header_size);
struct mbuf *dtom(Slirp *, void *);
+void *mtod_check(struct mbuf *, size_t len);
+void *m_end(struct mbuf *);
static inline void ifs_init(struct mbuf *ifm)
{
diff --git a/src/misc.c b/src/misc.c
index 48f180b..ba5fb51 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -82,7 +82,7 @@ static int slirp_socketpair_with_oob(int sv[2])
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = 0,
- .sin_addr.s_addr = INADDR_ANY,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
socklen_t addrlen = sizeof(addr);
int ret, s;
diff --git a/src/slirp.c b/src/slirp.c
index 5d60cb5..378a70b 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -143,6 +143,10 @@ static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,
union res_sockaddr_union servers[NI_MAXSERV];
int count;
int found;
+ void *addr;
+
+ // we only support IPv4 and IPv4, we assume it's one or the other
+ assert(af == AF_INET || af == AF_INET6);
if (res_ninit(&state) != 0) {
return -1;
@@ -155,11 +159,16 @@ static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,
if (af == servers[i].sin.sin_family) {
found++;
}
+ if (af == AF_INET) {
+ addr = &servers[i].sin.sin_addr;
+ } else { // af == AF_INET6
+ addr = &servers[i].sin6.sin6_addr;
+ }
// we use the first found entry
if (found == 1) {
- memcpy(pdns_addr, &servers[i].sin.sin_addr, addrlen);
- memcpy(cached_addr, &servers[i].sin.sin_addr, addrlen);
+ memcpy(pdns_addr, addr, addrlen);
+ memcpy(cached_addr, addr, addrlen);
if (scope_id) {
*scope_id = 0;
}
@@ -171,10 +180,7 @@ static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,
break;
} else if (slirp_debug & DBG_MISC) {
char s[INET6_ADDRSTRLEN];
- const char *res = inet_ntop(servers[i].sin.sin_family,
- &servers[i].sin.sin_addr,
- s,
- sizeof(s));
+ const char *res = inet_ntop(af, addr, s, sizeof(s));
if (!res) {
res = " (string conversion error)";
}
@@ -182,7 +188,7 @@ static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,
}
}
- res_nclose(&state);
+ res_ndestroy(&state);
if (!found)
return -1;
return 0;
@@ -218,6 +224,12 @@ int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)
#else // !defined(_WIN32) && !defined(__APPLE__)
+#if defined(__HAIKU__)
+#define RESOLV_CONF_PATH "/boot/system/settings/network/resolv.conf"
+#else
+#define RESOLV_CONF_PATH "/etc/resolv.conf"
+#endif
+
static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
socklen_t addrlen, struct stat *cached_stat,
unsigned *cached_time)
@@ -228,7 +240,7 @@ static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,
return 0;
}
old_stat = *cached_stat;
- if (stat("/etc/resolv.conf", cached_stat) != 0) {
+ if (stat(RESOLV_CONF_PATH, cached_stat) != 0) {
return -1;
}
if (cached_stat->st_dev == old_stat.st_dev &&
@@ -256,7 +268,7 @@ static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,
unsigned if_index;
assert(sizeof(tmp_addr) >= addrlen);
- f = fopen("/etc/resolv.conf", "r");
+ f = fopen(RESOLV_CONF_PATH, "r");
if (!f)
return -1;
@@ -601,7 +613,10 @@ void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,
/*
* Set for reading (and urgent data) if we are connected, can
- * receive more, and we have room for it XXX /2 ?
+ * receive more, and we have room for it.
+ *
+ * If sb is already half full, we will wait for the guest to consume it,
+ * and notify again in sbdrop() when the sb becomes less than half full.
*/
if (CONN_CANFRCV(so) &&
(so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) {
@@ -1347,6 +1362,8 @@ size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
}
if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) {
+ /* If the sb is already half full, we will wait for the guest to consume it,
+ * and notify again in sbdrop() when the sb becomes less than half full. */
return 0;
}
diff --git a/src/slirp.h b/src/slirp.h
index e669383..89d79f3 100644
--- a/src/slirp.h
+++ b/src/slirp.h
@@ -20,10 +20,8 @@
#include <iphlpapi.h>
#else
-#if !defined(__HAIKU__)
#define O_BINARY 0
#endif
-#endif
#ifndef _WIN32
#include <sys/uio.h>
diff --git a/src/tcp_input.c b/src/tcp_input.c
index 36a4844..45706fe 100644
--- a/src/tcp_input.c
+++ b/src/tcp_input.c
@@ -218,6 +218,9 @@ void tcp_input(struct mbuf *m, int iphlen, struct socket *inso,
DEBUG_CALL("tcp_input");
DEBUG_ARG("m = %p iphlen = %2d inso = %p", m, iphlen, inso);
+ memset(&lhost, 0, sizeof(struct sockaddr_storage));
+ memset(&fhost, 0, sizeof(struct sockaddr_storage));
+
/*
* If called with m == 0, then we're continuing the connect
*/
diff --git a/src/tcp_subr.c b/src/tcp_subr.c
index 600cfa1..7c63f49 100644
--- a/src/tcp_subr.c
+++ b/src/tcp_subr.c
@@ -464,7 +464,7 @@ void tcp_connect(struct socket *inso)
Slirp *slirp = inso->slirp;
struct socket *so;
struct sockaddr_storage addr;
- socklen_t addrlen = sizeof(struct sockaddr_storage);
+ socklen_t addrlen;
struct tcpcb *tp;
int s, opt, ret;
/* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */
@@ -473,7 +473,17 @@ void tcp_connect(struct socket *inso)
DEBUG_CALL("tcp_connect");
DEBUG_ARG("inso = %p", inso);
- ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, sizeof(inso->lhost.ss), addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
+ switch (inso->lhost.ss.ss_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, addrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV);
g_assert(ret == 0);
DEBUG_ARG("ip = [%s]:%s", addrstr, portstr);
DEBUG_ARG("so_state = 0x%x", inso->so_state);
@@ -494,6 +504,7 @@ void tcp_connect(struct socket *inso)
* us again until the guest address is available.
*/
DEBUG_MISC(" guest address not available yet");
+ addrlen = sizeof(addr);
s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);
if (s >= 0) {
close(s);
@@ -518,6 +529,7 @@ void tcp_connect(struct socket *inso)
tcp_mss(sototcpcb(so), 0);
+ addrlen = sizeof(addr);
s = accept(inso->s, (struct sockaddr *)&addr, &addrlen);
if (s < 0) {
tcp_close(sototcpcb(so)); /* This will sofree() as well */
diff --git a/src/tftp.c b/src/tftp.c
index c6950ee..a19c889 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -50,7 +50,7 @@ static void tftp_session_terminate(struct tftp_session *spt)
}
static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
- struct tftp_t *tp)
+ struct tftphdr *hdr)
{
struct tftp_session *spt;
int k;
@@ -75,7 +75,7 @@ found:
memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas));
spt->fd = -1;
spt->block_size = 512;
- spt->client_port = tp->udp.uh_sport;
+ spt->client_port = hdr->udp.uh_sport;
spt->slirp = slirp;
tftp_session_update(spt);
@@ -84,7 +84,7 @@ found:
}
static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
- struct tftp_t *tp)
+ struct tftphdr *hdr)
{
struct tftp_session *spt;
int k;
@@ -94,7 +94,7 @@ static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
if (tftp_session_in_use(spt)) {
if (sockaddr_equal(&spt->client_addr, srcsas)) {
- if (spt->client_port == tp->udp.uh_sport) {
+ if (spt->client_port == hdr->udp.uh_sport) {
return k;
}
}
@@ -148,13 +148,13 @@ static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt,
}
static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
- struct tftp_t *recv_tp)
+ struct tftphdr *hdr)
{
if (spt->client_addr.ss_family == AF_INET6) {
struct sockaddr_in6 sa6, da6;
sa6.sin6_addr = spt->slirp->vhost_addr6;
- sa6.sin6_port = recv_tp->udp.uh_dport;
+ sa6.sin6_port = hdr->udp.uh_dport;
da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
da6.sin6_port = spt->client_port;
@@ -163,7 +163,7 @@ static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
struct sockaddr_in sa4, da4;
sa4.sin_addr = spt->slirp->vhost_addr;
- sa4.sin_port = recv_tp->udp.uh_dport;
+ sa4.sin_port = hdr->udp.uh_dport;
da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
da4.sin_port = spt->client_port;
@@ -185,14 +185,14 @@ static int tftp_send_oack(struct tftp_session *spt, const char *keys[],
tp = tftp_prep_mbuf_data(spt, m);
- tp->tp_op = htons(TFTP_OACK);
+ tp->hdr.tp_op = htons(TFTP_OACK);
for (i = 0; i < nb; i++) {
n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", keys[i]);
n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", values[i]);
}
- m->m_len = G_SIZEOF_MEMBER(struct tftp_t, tp_op) + n;
- tftp_udp_output(spt, m, recv_tp);
+ m->m_len = G_SIZEOF_MEMBER(struct tftp_t, hdr.tp_op) + n;
+ tftp_udp_output(spt, m, &recv_tp->hdr);
return 0;
}
@@ -213,21 +213,21 @@ static void tftp_send_error(struct tftp_session *spt, uint16_t errorcode,
tp = tftp_prep_mbuf_data(spt, m);
- tp->tp_op = htons(TFTP_ERROR);
+ tp->hdr.tp_op = htons(TFTP_ERROR);
tp->x.tp_error.tp_error_code = htons(errorcode);
slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg),
msg);
m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 +
strlen(msg) - sizeof(struct udphdr);
- tftp_udp_output(spt, m, recv_tp);
+ tftp_udp_output(spt, m, &recv_tp->hdr);
out:
tftp_session_terminate(spt);
}
static void tftp_send_next_block(struct tftp_session *spt,
- struct tftp_t *recv_tp)
+ struct tftphdr *hdr)
{
struct mbuf *m;
struct tftp_t *tp;
@@ -241,7 +241,7 @@ static void tftp_send_next_block(struct tftp_session *spt,
tp = tftp_prep_mbuf_data(spt, m);
- tp->tp_op = htons(TFTP_DATA);
+ tp->hdr.tp_op = htons(TFTP_DATA);
tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf,
@@ -259,7 +259,7 @@ static void tftp_send_next_block(struct tftp_session *spt,
m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes) -
sizeof(struct udphdr);
- tftp_udp_output(spt, m, recv_tp);
+ tftp_udp_output(spt, m, hdr);
if (nobytes == spt->block_size) {
tftp_session_update(spt);
@@ -282,12 +282,12 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
int nb_options = 0;
/* check if a session already exists and if so terminate it */
- s = tftp_session_find(slirp, srcsas, tp);
+ s = tftp_session_find(slirp, srcsas, &tp->hdr);
if (s >= 0) {
tftp_session_terminate(&slirp->tftp_sessions[s]);
}
- s = tftp_session_allocate(slirp, srcsas, tp);
+ s = tftp_session_allocate(slirp, srcsas, &tp->hdr);
if (s < 0) {
return;
@@ -413,29 +413,29 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas,
}
spt->block_nr = 0;
- tftp_send_next_block(spt, tp);
+ tftp_send_next_block(spt, &tp->hdr);
}
static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
- struct tftp_t *tp, int pktlen)
+ struct tftphdr *hdr)
{
int s;
- s = tftp_session_find(slirp, srcsas, tp);
+ s = tftp_session_find(slirp, srcsas, hdr);
if (s < 0) {
return;
}
- tftp_send_next_block(&slirp->tftp_sessions[s], tp);
+ tftp_send_next_block(&slirp->tftp_sessions[s], hdr);
}
static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
- struct tftp_t *tp, int pktlen)
+ struct tftphdr *hdr)
{
int s;
- s = tftp_session_find(slirp, srcsas, tp);
+ s = tftp_session_find(slirp, srcsas, hdr);
if (s < 0) {
return;
@@ -446,19 +446,25 @@ static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
{
- struct tftp_t *tp = (struct tftp_t *)m->m_data;
+ struct tftphdr *hdr = mtod_check(m, sizeof(struct tftphdr));
+
+ if (hdr == NULL) {
+ return;
+ }
- switch (ntohs(tp->tp_op)) {
+ switch (ntohs(hdr->tp_op)) {
case TFTP_RRQ:
- tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
+ tftp_handle_rrq(m->slirp, srcsas,
+ mtod(m, struct tftp_t *),
+ m->m_len);
break;
case TFTP_ACK:
- tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
+ tftp_handle_ack(m->slirp, srcsas, hdr);
break;
case TFTP_ERROR:
- tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
+ tftp_handle_error(m->slirp, srcsas, hdr);
break;
}
}
diff --git a/src/tftp.h b/src/tftp.h
index 6d75478..cafab03 100644
--- a/src/tftp.h
+++ b/src/tftp.h
@@ -20,9 +20,13 @@
#define TFTP_FILENAME_MAX 512
#define TFTP_BLOCKSIZE_MAX 1428
-struct tftp_t {
+struct tftphdr {
struct udphdr udp;
uint16_t tp_op;
+} SLIRP_PACKED;
+
+struct tftp_t {
+ struct tftphdr hdr;
union {
struct {
uint16_t tp_block_nr;
diff --git a/src/udp.c b/src/udp.c
index 767ca85..06b7b7d 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -96,7 +96,10 @@ void udp_input(register struct mbuf *m, int iphlen)
/*
* Get IP and UDP header together in first mbuf.
*/
- ip = mtod(m, struct ip *);
+ ip = mtod_check(m, iphlen + sizeof(struct udphdr));
+ if (ip == NULL) {
+ goto bad;
+ }
uh = (struct udphdr *)((char *)ip + iphlen);
/*
diff --git a/src/udp6.c b/src/udp6.c
index 18ce998..efeac5c 100644
--- a/src/udp6.c
+++ b/src/udp6.c
@@ -31,7 +31,10 @@ void udp6_input(struct mbuf *m)
ip = mtod(m, struct ip6 *);
m->m_len -= iphlen;
m->m_data += iphlen;
- uh = mtod(m, struct udphdr *);
+ uh = mtod_check(m, sizeof(struct udphdr));
+ if (uh == NULL) {
+ goto bad;
+ }
m->m_len += iphlen;
m->m_data -= iphlen;