diff options
-rw-r--r-- | .gitlab-ci.yml | 33 | ||||
-rw-r--r-- | CHANGELOG.md | 22 | ||||
-rw-r--r-- | meson.build | 9 | ||||
-rw-r--r-- | src/bootp.c | 36 | ||||
-rw-r--r-- | src/bootp.h | 2 | ||||
-rw-r--r-- | src/libslirp.h | 3 | ||||
-rw-r--r-- | src/mbuf.c | 16 | ||||
-rw-r--r-- | src/mbuf.h | 2 | ||||
-rw-r--r-- | src/misc.c | 2 | ||||
-rw-r--r-- | src/slirp.c | 37 | ||||
-rw-r--r-- | src/slirp.h | 2 | ||||
-rw-r--r-- | src/tcp_input.c | 3 | ||||
-rw-r--r-- | src/tcp_subr.c | 16 | ||||
-rw-r--r-- | src/tftp.c | 62 | ||||
-rw-r--r-- | src/tftp.h | 6 | ||||
-rw-r--r-- | src/udp.c | 5 | ||||
-rw-r--r-- | src/udp6.c | 5 |
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; @@ -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; +} @@ -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) { @@ -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 */ @@ -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; } } @@ -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; @@ -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); /* @@ -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; |