diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c index 230cda96..b7fdf7c4 100644 --- a/src/core/ipv4/ip4.c +++ b/src/core/ipv4/ip4.c @@ -320,9 +320,9 @@ ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) /* Incrementally update the IP checksum. */ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { - IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1)); } else { - IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100))); } LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", @@ -414,7 +414,7 @@ ip4_input_accept(struct netif *netif) err_t ip4_input(struct pbuf *p, struct netif *inp) { - struct ip_hdr *iphdr; + const struct ip_hdr *iphdr; struct netif *netif; u16_t iphdr_hlen; u16_t iphdr_len; @@ -444,10 +444,8 @@ ip4_input(struct pbuf *p, struct netif *inp) } #endif - /* obtain IP header length in number of 32-bit words */ - iphdr_hlen = IPH_HL(iphdr); - /* calculate IP header length in bytes */ - iphdr_hlen *= 4; + /* obtain IP header length in bytes */ + iphdr_hlen = IPH_HL_BYTES(iphdr); /* obtain ip length in bytes */ iphdr_len = lwip_ntohs(IPH_LEN(iphdr)); @@ -565,7 +563,7 @@ ip4_input(struct pbuf *p, struct netif *inp) if (netif == NULL) { /* remote port is DHCP server? */ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { - struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen); LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n", lwip_ntohs(udphdr->dest))); if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { @@ -608,7 +606,7 @@ ip4_input(struct pbuf *p, struct netif *inp) /* non-broadcast packet? */ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) { /* try to forward IP packet on (other) interfaces */ - ip4_forward(p, iphdr, inp); + ip4_forward(p, (struct ip_hdr *)p->payload, inp); } else #endif /* IP_FORWARD */ { @@ -630,7 +628,7 @@ ip4_input(struct pbuf *p, struct netif *inp) if (p == NULL) { return ERR_OK; } - iphdr = (struct ip_hdr *)p->payload; + iphdr = (const struct ip_hdr *)p->payload; #else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", @@ -669,14 +667,14 @@ ip4_input(struct pbuf *p, struct netif *inp) ip_data.current_netif = netif; ip_data.current_input_netif = inp; ip_data.current_ip4_header = iphdr; - ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr); #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) #endif /* LWIP_RAW */ { - pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */ + pbuf_header(p, (s16_t)-(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */ switch (IPH_PROTO(iphdr)) { #if LWIP_UDP @@ -710,8 +708,7 @@ ip4_input(struct pbuf *p, struct netif *inp) /* send ICMP destination protocol unreachable unless is was a broadcast */ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) && !ip4_addr_ismulticast(ip4_current_dest_addr())) { - pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */ - p->payload = iphdr; + pbuf_header_force(p, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */ icmp_dest_unreach(p, ICMP_DUR_PROTO); } #endif /* LWIP_ICMP */ @@ -839,11 +836,18 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d #if CHECKSUM_GEN_IP_INLINE int i; #endif /* CHECKSUM_GEN_IP_INLINE */ + if (optlen > (IP_HLEN_MAX - IP_HLEN)) { + /* optlen too long */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n")); + IP_STATS_INC(ip.err); + MIB2_STATS_INC(mib2.ipoutdiscards); + return ERR_VAL; + } /* round up to a multiple of 4 */ - optlen_aligned = ((optlen + 3) & ~3); - ip_hlen += optlen_aligned; + optlen_aligned = (u16_t)((optlen + 3) & ~3); + ip_hlen = (u16_t)(ip_hlen + optlen_aligned); /* First write in the IP options */ - if (pbuf_header(p, optlen_aligned)) { + if (pbuf_header(p, (s16_t)optlen_aligned)) { LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n")); IP_STATS_INC(ip.err); MIB2_STATS_INC(mib2.ipoutdiscards); @@ -852,7 +856,7 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d MEMCPY(p->payload, ip_options, optlen); if (optlen < optlen_aligned) { /* zero the remaining bytes */ - memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + memset(((char*)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen)); } #if CHECKSUM_GEN_IP_INLINE for (i = 0; i < optlen_aligned/2; i++) { @@ -934,6 +938,12 @@ ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *d #endif /* CHECKSUM_GEN_IP_INLINE */ } else { /* IP header already included in p */ + if (p->len < IP_HLEN) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n")); + IP_STATS_INC(ip.err); + MIB2_STATS_INC(mib2.ipoutdiscards); + return ERR_BUF; + } iphdr = (struct ip_hdr *)p->payload; ip4_addr_copy(dest_addr, iphdr->dest); dest = &dest_addr; diff --git a/src/core/ipv4/ip4_frag.c b/src/core/ipv4/ip4_frag.c index 57fb44cb..bf9fc1b8 100644 --- a/src/core/ipv4/ip4_frag.c +++ b/src/core/ipv4/ip4_frag.c @@ -182,7 +182,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p icmp_time_exceeded(p, ICMP_TE_FRAG); clen = pbuf_clen(p); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); - pbufs_freed += clen; + pbufs_freed = (u16_t)(pbufs_freed + clen); pbuf_free(p); } #endif /* LWIP_ICMP */ @@ -198,13 +198,13 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p p = iprh->next_pbuf; clen = pbuf_clen(pcur); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); - pbufs_freed += clen; + pbufs_freed = (u16_t)(pbufs_freed + clen); pbuf_free(pcur); } /* Then, unchain the struct ip_reassdata from the list and free it. */ ip_reass_dequeue_datagram(ipr, prev); - LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); - ip_reass_pbufcount -= pbufs_freed; + LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed); return pbufs_freed; } @@ -340,14 +340,21 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct { struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; struct pbuf *q; - u16_t offset, len; + u16_t offset, len, clen; + u8_t hlen; struct ip_hdr *fraghdr; int valid = 1; /* Extract length and fragment offset from current fragment */ fraghdr = (struct ip_hdr*)new_p->payload; - len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; - offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = lwip_ntohs(IPH_LEN(fraghdr)); + hlen = IPH_HL_BYTES(fraghdr); + if (hlen > len) { + /* invalid datagram */ + goto freepbuf; + } + len = (u16_t)(len - hlen); + offset = IPH_OFFSET_BYTES(fraghdr); /* overwrite the fragment's ip header from the pbuf with our helper struct, * and setup the embedded helper structure. */ @@ -357,7 +364,11 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct iprh = (struct ip_reass_helper*)new_p->payload; iprh->next_pbuf = NULL; iprh->start = offset; - iprh->end = offset + len; + iprh->end = (u16_t)(offset + len); + if (iprh->end < offset) { + /* u16_t overflow, cannot handle this */ + goto freepbuf; + } /* Iterate through until we either get to the end of the list (append), * or we find one with a larger offset (insert). */ @@ -468,7 +479,9 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct return 0; /* not yet valid! */ #if IP_REASS_CHECK_OVERLAP freepbuf: - ip_reass_pbufcount -= pbuf_clen(new_p); + clen = pbuf_clen(new_p); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen); + ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen); pbuf_free(new_p); return 0; #endif /* IP_REASS_CHECK_OVERLAP */ @@ -488,6 +501,7 @@ ip4_reass(struct pbuf *p) struct ip_reassdata *ipr; struct ip_reass_helper *iprh; u16_t offset, len, clen; + u8_t hlen; IPFRAG_STATS_INC(ip_frag.recv); MIB2_STATS_INC(mib2.ipreasmreqds); @@ -500,8 +514,14 @@ ip4_reass(struct pbuf *p) goto nullreturn; } - offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; - len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = IPH_OFFSET_BYTES(fraghdr); + len = lwip_ntohs(IPH_LEN(fraghdr)); + hlen = IPH_HL_BYTES(fraghdr); + if (hlen > len) { + /* invalid datagram */ + goto nullreturn; + } + len = (u16_t)(len - hlen); /* Check if we are allowed to enqueue more datagrams. */ clen = pbuf_clen(p); @@ -553,16 +573,22 @@ ip4_reass(struct pbuf *p) } } /* Track the current number of pbufs current 'in-flight', in order to limit - the number of fragments that may be enqueued at any one time */ - ip_reass_pbufcount += clen; + the number of fragments that may be enqueued at any one time + (overflow checked by testing against IP_REASS_MAX_PBUFS) */ + ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen); /* At this point, we have either created a new entry or pointing * to an existing one */ /* check for 'no more fragments', and update queue entry*/ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + u16_t datagram_len = (u16_t)(offset + len); + if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) { + /* u16_t overflow, cannot handle this */ + goto nullreturn; + } + ipr->datagram_len = datagram_len; ipr->flags |= IP_REASS_FLAG_LASTFRAG; - ipr->datagram_len = offset + len; LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: last fragment seen, total len %"S16_F"\n", ipr->datagram_len)); @@ -573,7 +599,7 @@ ip4_reass(struct pbuf *p) struct ip_reassdata *ipr_prev; /* the totally last fragment (flag more fragments = 0) was received at least * once AND all fragments are received */ - ipr->datagram_len += IP_HLEN; + u16_t datagram_len = (u16_t)(ipr->datagram_len + IP_HLEN); /* save the second pbuf before copying the header over the pointer */ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; @@ -581,7 +607,7 @@ ip4_reass(struct pbuf *p) /* copy the original ip header back to the first pbuf */ fraghdr = (struct ip_hdr*)(ipr->p->payload); SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); - IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len)); + IPH_LEN_SET(fraghdr, lwip_htons(datagram_len)); IPH_OFFSET_SET(fraghdr, 0); IPH_CHKSUM_SET(fraghdr, 0); /* @todo: do we need to set/calculate the correct checksum? */ @@ -618,7 +644,9 @@ ip4_reass(struct pbuf *p) ip_reass_dequeue_datagram(ipr, ipr_prev); /* and adjust the number of pbufs currently queued for reassembly. */ - ip_reass_pbufcount -= pbuf_clen(p); + clen = pbuf_clen(p); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen); + ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen); MIB2_STATS_INC(mib2.ipreasmoks); @@ -692,7 +720,7 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) #endif struct ip_hdr *original_iphdr; struct ip_hdr *iphdr; - const u16_t nfb = (netif->mtu - IP_HLEN) / 8; + const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8); u16_t left, fragsize; u16_t ofo; int last; @@ -701,18 +729,19 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) original_iphdr = (struct ip_hdr *)p->payload; iphdr = original_iphdr; - LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL); + LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL_BYTES(iphdr) == IP_HLEN, return ERR_VAL); + LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL); /* Save original offset */ tmp = lwip_ntohs(IPH_OFFSET(iphdr)); ofo = tmp & IP_OFFMASK; LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL); - left = p->tot_len - IP_HLEN; + left = (u16_t)(p->tot_len - IP_HLEN); while (left) { /* Fill this fragment */ - fragsize = LWIP_MIN(left, nfb * 8); + fragsize = LWIP_MIN(left, (u16_t)(nfb * 8)); #if LWIP_NETIF_TX_SINGLE_PBUF rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM); @@ -748,7 +777,8 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) left_to_copy = fragsize; while (left_to_copy) { struct pbuf_custom_ref *pcr; - u16_t plen = p->len - poff; + u16_t plen = (u16_t)(p->len - poff); + LWIP_ASSERT("p->len >= poff", p->len >= poff); newpbuflen = LWIP_MIN(left_to_copy, plen); /* Is this pbuf already empty? */ if (!newpbuflen) { @@ -777,13 +807,13 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) * so that it is removed when pbuf_dechain is later called on rambuf. */ pbuf_cat(rambuf, newpbuf); - left_to_copy -= newpbuflen; + left_to_copy = (u16_t)(left_to_copy - newpbuflen); if (left_to_copy) { poff = 0; p = p->next; } } - poff += newpbuflen; + poff = (u16_t)(poff + newpbuflen); #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ /* Correct header */ @@ -795,7 +825,7 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) tmp = tmp | IP_MF; } IPH_OFFSET_SET(iphdr, lwip_htons(tmp)); - IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN)); + IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + IP_HLEN))); IPH_CHKSUM_SET(iphdr, 0); #if CHECKSUM_GEN_IP IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { @@ -817,8 +847,8 @@ ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest) */ pbuf_free(rambuf); - left -= fragsize; - ofo += nfb; + left = (u16_t)(left - fragsize); + ofo = (u16_t)(ofo + nfb); } MIB2_STATS_INC(mib2.ipfragoks); return ERR_OK; diff --git a/src/include/lwip/ip.h b/src/include/lwip/ip.h index 33eecebb..128c62b3 100644 --- a/src/include/lwip/ip.h +++ b/src/include/lwip/ip.h @@ -112,7 +112,7 @@ struct ip_globals struct netif *current_input_netif; #if LWIP_IPV4 /** Header of the input packet currently being processed. */ - struct ip_hdr *current_ip4_header; + const struct ip_hdr *current_ip4_header; #endif /* LWIP_IPV4 */ #if LWIP_IPV6 /** Header of the input IPv6 packet currently being processed. */ @@ -148,7 +148,7 @@ extern struct ip_globals ip_data; /** Get the IPv4 header of the current packet. * This function must only be called from a receive callback (udp_recv, * raw_recv, tcp_accept). It will return NULL otherwise. */ -#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header)) +#define ip4_current_header() ip_data.current_ip4_header /** Get the IPv6 header of the current packet. * This function must only be called from a receive callback (udp_recv, * raw_recv, tcp_accept). It will return NULL otherwise. */ @@ -177,7 +177,7 @@ extern struct ip_globals ip_data; /** Get the IPv4 header of the current packet. * This function must only be called from a receive callback (udp_recv, * raw_recv, tcp_accept). It will return NULL otherwise. */ -#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header)) +#define ip4_current_header() ip_data.current_ip4_header /** Always returns FALSE when only supporting IPv4 only */ #define ip_current_is_v6() 0 /** Get the transport layer protocol */ diff --git a/src/include/lwip/prot/ip4.h b/src/include/lwip/prot/ip4.h index 7eb4201d..f80a9b01 100644 --- a/src/include/lwip/prot/ip4.h +++ b/src/include/lwip/prot/ip4.h @@ -62,6 +62,8 @@ typedef struct ip4_addr_packed ip4_addr_p_t; /* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */ #define IP_HLEN 20 +/* Maximum size of the IPv4 header with options. */ +#define IP_HLEN_MAX 60 #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" @@ -106,6 +108,7 @@ PACK_STRUCT_END #define IPH_LEN(hdr) ((hdr)->_len) #define IPH_ID(hdr) ((hdr)->_id) #define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_OFFSET_BYTES(hdr) ((u16_t)((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8U)) #define IPH_TTL(hdr) ((hdr)->_ttl) #define IPH_PROTO(hdr) ((hdr)->_proto) #define IPH_CHKSUM(hdr) ((hdr)->_chksum)