diff mbox

[1/3] ipv6: Do not use unitialized MAC address array

Message ID 1460023795-27136-2-git-send-email-thuth@redhat.com
State Accepted
Headers show

Commit Message

Thomas Huth April 7, 2016, 10:09 a.m. UTC
The code in send_ipv6() currently basically looks like this:

	uint8_t *mac_addr, mac[6];
	mac_addr = mac;
	...
	n = find_neighbor (&ip_dst);
	if (ip6_is_multicast (&ip_dst)) {
		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
	}
	else {
		// Check if the MAC address is already cached
		if (n) {
			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
				memcpy (mac_addr, &(n->mac), ETH_ALEN);
			/* XXX */
		}
		...
	}
	...
	fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(),
		     mac_addr);

That means mac_addr initially points to the uninitialized mac[6]
array on the stack. Now if there was already an entry in the neighbor
cache, but the MAC address has not been determined yet, that
uninitialized array could be used as final MAC address for fill_ethhdr()
(since there is no "else" path at the spot marked with XXX above),
resulting in random data in the MAC address field of the ethernet packet.

Let's fix this issue by letting mac_addr point to the null_mac by
default instead, so that it never points to invalid data. Also
rename mac[6] to mc_mac[6] to make it clear that this array is
only used for storing the multicast mac address.

Signed-off-by: Thomas Huth <thuth@redhat.com>
---
 clients/net-snk/app/netlib/ipv6.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Comments

Nikunj A Dadhania April 11, 2016, 5:32 a.m. UTC | #1
Thomas Huth <thuth@redhat.com> writes:

> The code in send_ipv6() currently basically looks like this:
>
> 	uint8_t *mac_addr, mac[6];
> 	mac_addr = mac;
> 	...
> 	n = find_neighbor (&ip_dst);
> 	if (ip6_is_multicast (&ip_dst)) {
> 		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
> 	}
> 	else {
> 		// Check if the MAC address is already cached
> 		if (n) {
> 			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
> 				memcpy (mac_addr, &(n->mac), ETH_ALEN);
> 			/* XXX */
> 		}
> 		...
> 	}
> 	...
> 	fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(),
> 		     mac_addr);
>
> That means mac_addr initially points to the uninitialized mac[6]
> array on the stack. Now if there was already an entry in the neighbor
> cache, but the MAC address has not been determined yet, that
> uninitialized array could be used as final MAC address for fill_ethhdr()
> (since there is no "else" path at the spot marked with XXX above),
> resulting in random data in the MAC address field of the ethernet packet.
>
> Let's fix this issue by letting mac_addr point to the null_mac by
> default instead, so that it never points to invalid data. Also
> rename mac[6] to mc_mac[6] to make it clear that this array is
> only used for storing the multicast mac address.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>

Reviewed-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>


> ---
>  clients/net-snk/app/netlib/ipv6.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/clients/net-snk/app/netlib/ipv6.c b/clients/net-snk/app/netlib/ipv6.c
> index 348e79d..5a307ca 100644
> --- a/clients/net-snk/app/netlib/ipv6.c
> +++ b/clients/net-snk/app/netlib/ipv6.c
> @@ -491,9 +491,9 @@ int send_ipv6(int fd, void* buffer, int len)
>  	struct udphdr *udph;
>  	struct icmp6hdr *icmp6h;
>  	ip6_addr_t ip_dst;
> -	uint8_t *mac_addr, mac[6];
> +	uint8_t *mac_addr, mc_mac[6];
>
> -	mac_addr = mac;
> +	mac_addr = null_mac;
>
>  	ip6h    = (struct ip6hdr *) buffer;
>  	udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct ip6hdr));
> @@ -526,13 +526,13 @@ int send_ipv6(int fd, void* buffer, int len)
>
>  	// If address is a multicast address, create a proper mac address
>  	if (ip6_is_multicast (&ip_dst)) {
> -		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
> +		mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
>  	}
>  	else {
>  		// Check if the MAC address is already cached
>  		if (n) {
>  			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
> -				memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */
> +				mac_addr = n->mac;		/* found it */
>  		} else if (!is_ip6addr_in_my_net(&ip_dst)) {
>  			struct router *gw;
>  			gw = ipv6_get_default_router(&ip6h->src);
> -- 
> 1.8.3.1
Alexey Kardashevskiy April 12, 2016, 3:43 a.m. UTC | #2
On 04/07/2016 08:09 PM, Thomas Huth wrote:
> The code in send_ipv6() currently basically looks like this:
>
> 	uint8_t *mac_addr, mac[6];
> 	mac_addr = mac;
> 	...
> 	n = find_neighbor (&ip_dst);
> 	if (ip6_is_multicast (&ip_dst)) {
> 		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
> 	}
> 	else {
> 		// Check if the MAC address is already cached
> 		if (n) {
> 			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
> 				memcpy (mac_addr, &(n->mac), ETH_ALEN);
> 			/* XXX */
> 		}
> 		...
> 	}
> 	...
> 	fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(),
> 		     mac_addr);
>
> That means mac_addr initially points to the uninitialized mac[6]
> array on the stack. Now if there was already an entry in the neighbor
> cache, but the MAC address has not been determined yet, that
> uninitialized array could be used as final MAC address for fill_ethhdr()
> (since there is no "else" path at the spot marked with XXX above),
> resulting in random data in the MAC address field of the ethernet packet.
>
> Let's fix this issue by letting mac_addr point to the null_mac by
> default instead, so that it never points to invalid data. Also
> rename mac[6] to mc_mac[6] to make it clear that this array is
> only used for storing the multicast mac address.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
> ---
>   clients/net-snk/app/netlib/ipv6.c | 8 ++++----
>   1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/clients/net-snk/app/netlib/ipv6.c b/clients/net-snk/app/netlib/ipv6.c
> index 348e79d..5a307ca 100644
> --- a/clients/net-snk/app/netlib/ipv6.c
> +++ b/clients/net-snk/app/netlib/ipv6.c
> @@ -491,9 +491,9 @@ int send_ipv6(int fd, void* buffer, int len)
>   	struct udphdr *udph;
>   	struct icmp6hdr *icmp6h;
>   	ip6_addr_t ip_dst;
> -	uint8_t *mac_addr, mac[6];
> +	uint8_t *mac_addr, mc_mac[6];
>
> -	mac_addr = mac;
> +	mac_addr = null_mac;
>
>   	ip6h    = (struct ip6hdr *) buffer;
>   	udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct ip6hdr));
> @@ -526,13 +526,13 @@ int send_ipv6(int fd, void* buffer, int len)
>
>   	// If address is a multicast address, create a proper mac address
>   	if (ip6_is_multicast (&ip_dst)) {
> -		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
> +		mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
>   	}
>   	else {
>   		// Check if the MAC address is already cached
>   		if (n) {
>   			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
> -				memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */
> +				mac_addr = n->mac;		/* found it */


btw there is another thing - sometime the check if mac is NULL is done by 
"if (mac_addr == null_mac)", sometime by memcmp, can we unify this?


>   		} else if (!is_ip6addr_in_my_net(&ip_dst)) {
>   			struct router *gw;
>   			gw = ipv6_get_default_router(&ip6h->src);
>
Thomas Huth April 12, 2016, 7:05 a.m. UTC | #3
On 12.04.2016 05:43, Alexey Kardashevskiy wrote:
> On 04/07/2016 08:09 PM, Thomas Huth wrote:
>> The code in send_ipv6() currently basically looks like this:
>>
>>     uint8_t *mac_addr, mac[6];
>>     mac_addr = mac;
>>     ...
>>     n = find_neighbor (&ip_dst);
>>     if (ip6_is_multicast (&ip_dst)) {
>>         mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
>>     }
>>     else {
>>         // Check if the MAC address is already cached
>>         if (n) {
>>             if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
>>                 memcpy (mac_addr, &(n->mac), ETH_ALEN);
>>             /* XXX */
>>         }
>>         ...
>>     }
>>     ...
>>     fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(),
>>              mac_addr);
>>
>> That means mac_addr initially points to the uninitialized mac[6]
>> array on the stack. Now if there was already an entry in the neighbor
>> cache, but the MAC address has not been determined yet, that
>> uninitialized array could be used as final MAC address for fill_ethhdr()
>> (since there is no "else" path at the spot marked with XXX above),
>> resulting in random data in the MAC address field of the ethernet packet.
>>
>> Let's fix this issue by letting mac_addr point to the null_mac by
>> default instead, so that it never points to invalid data. Also
>> rename mac[6] to mc_mac[6] to make it clear that this array is
>> only used for storing the multicast mac address.
>>
>> Signed-off-by: Thomas Huth <thuth@redhat.com>
>> ---
>>   clients/net-snk/app/netlib/ipv6.c | 8 ++++----
>>   1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/clients/net-snk/app/netlib/ipv6.c
>> b/clients/net-snk/app/netlib/ipv6.c
>> index 348e79d..5a307ca 100644
>> --- a/clients/net-snk/app/netlib/ipv6.c
>> +++ b/clients/net-snk/app/netlib/ipv6.c
>> @@ -491,9 +491,9 @@ int send_ipv6(int fd, void* buffer, int len)
>>       struct udphdr *udph;
>>       struct icmp6hdr *icmp6h;
>>       ip6_addr_t ip_dst;
>> -    uint8_t *mac_addr, mac[6];
>> +    uint8_t *mac_addr, mc_mac[6];
>>
>> -    mac_addr = mac;
>> +    mac_addr = null_mac;
>>
>>       ip6h    = (struct ip6hdr *) buffer;
>>       udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct
>> ip6hdr));
>> @@ -526,13 +526,13 @@ int send_ipv6(int fd, void* buffer, int len)
>>
>>       // If address is a multicast address, create a proper mac address
>>       if (ip6_is_multicast (&ip_dst)) {
>> -        mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
>> +        mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
>>       }
>>       else {
>>           // Check if the MAC address is already cached
>>           if (n) {
>>               if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
>> -                memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */
>> +                mac_addr = n->mac;        /* found it */
> 
> 
> btw there is another thing - sometime the check if mac is NULL is done
> by "if (mac_addr == null_mac)", sometime by memcmp, can we unify this?

The mac_addr == null_mac checks are of course only useful for the case
where mac_addr has been set to null_mac in this function before. The
memcmp is needed in the above chunk since it checks n->mac instead. So
if you want to unify all checks, I think you have to convert them to
memcmp(), but it won't work the other way round.

 Thomas
Alexey Kardashevskiy April 12, 2016, 7:25 a.m. UTC | #4
On 04/12/2016 05:05 PM, Thomas Huth wrote:
> On 12.04.2016 05:43, Alexey Kardashevskiy wrote:
>> On 04/07/2016 08:09 PM, Thomas Huth wrote:
>>> The code in send_ipv6() currently basically looks like this:
>>>
>>>      uint8_t *mac_addr, mac[6];
>>>      mac_addr = mac;
>>>      ...
>>>      n = find_neighbor (&ip_dst);
>>>      if (ip6_is_multicast (&ip_dst)) {
>>>          mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
>>>      }
>>>      else {
>>>          // Check if the MAC address is already cached
>>>          if (n) {
>>>              if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
>>>                  memcpy (mac_addr, &(n->mac), ETH_ALEN);
>>>              /* XXX */
>>>          }
>>>          ...
>>>      }
>>>      ...
>>>      fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(),
>>>               mac_addr);
>>>
>>> That means mac_addr initially points to the uninitialized mac[6]
>>> array on the stack. Now if there was already an entry in the neighbor
>>> cache, but the MAC address has not been determined yet, that
>>> uninitialized array could be used as final MAC address for fill_ethhdr()
>>> (since there is no "else" path at the spot marked with XXX above),
>>> resulting in random data in the MAC address field of the ethernet packet.
>>>
>>> Let's fix this issue by letting mac_addr point to the null_mac by
>>> default instead, so that it never points to invalid data. Also
>>> rename mac[6] to mc_mac[6] to make it clear that this array is
>>> only used for storing the multicast mac address.
>>>
>>> Signed-off-by: Thomas Huth <thuth@redhat.com>
>>> ---
>>>    clients/net-snk/app/netlib/ipv6.c | 8 ++++----
>>>    1 file changed, 4 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/clients/net-snk/app/netlib/ipv6.c
>>> b/clients/net-snk/app/netlib/ipv6.c
>>> index 348e79d..5a307ca 100644
>>> --- a/clients/net-snk/app/netlib/ipv6.c
>>> +++ b/clients/net-snk/app/netlib/ipv6.c
>>> @@ -491,9 +491,9 @@ int send_ipv6(int fd, void* buffer, int len)
>>>        struct udphdr *udph;
>>>        struct icmp6hdr *icmp6h;
>>>        ip6_addr_t ip_dst;
>>> -    uint8_t *mac_addr, mac[6];
>>> +    uint8_t *mac_addr, mc_mac[6];
>>>
>>> -    mac_addr = mac;
>>> +    mac_addr = null_mac;
>>>
>>>        ip6h    = (struct ip6hdr *) buffer;
>>>        udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct
>>> ip6hdr));
>>> @@ -526,13 +526,13 @@ int send_ipv6(int fd, void* buffer, int len)
>>>
>>>        // If address is a multicast address, create a proper mac address
>>>        if (ip6_is_multicast (&ip_dst)) {
>>> -        mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
>>> +        mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
>>>        }
>>>        else {
>>>            // Check if the MAC address is already cached
>>>            if (n) {
>>>                if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
>>> -                memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */
>>> +                mac_addr = n->mac;        /* found it */
>>
>>
>> btw there is another thing - sometime the check if mac is NULL is done
>> by "if (mac_addr == null_mac)", sometime by memcmp, can we unify this?
>
> The mac_addr == null_mac checks are of course only useful for the case
> where mac_addr has been set to null_mac in this function before.


I am just reading the resulting code and afaict you could return from 
send_ipv6() right where you assign null_mac to mac_addr; otherwise I get 
impression that n->mac can possibly point to null_mac or 
ip6_to_multicast_mac() can return null_mac (but neither can).


> The
> memcmp is needed in the above chunk since it checks n->mac instead. So
> if you want to unify all checks, I think you have to convert them to
> memcmp(), but it won't work the other way round.


Sure, memcmp() is the way to go.
diff mbox

Patch

diff --git a/clients/net-snk/app/netlib/ipv6.c b/clients/net-snk/app/netlib/ipv6.c
index 348e79d..5a307ca 100644
--- a/clients/net-snk/app/netlib/ipv6.c
+++ b/clients/net-snk/app/netlib/ipv6.c
@@ -491,9 +491,9 @@  int send_ipv6(int fd, void* buffer, int len)
 	struct udphdr *udph;
 	struct icmp6hdr *icmp6h;
 	ip6_addr_t ip_dst;
-	uint8_t *mac_addr, mac[6];
+	uint8_t *mac_addr, mc_mac[6];
 
-	mac_addr = mac;
+	mac_addr = null_mac;
 
 	ip6h    = (struct ip6hdr *) buffer;
 	udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct ip6hdr));
@@ -526,13 +526,13 @@  int send_ipv6(int fd, void* buffer, int len)
 
 	// If address is a multicast address, create a proper mac address
 	if (ip6_is_multicast (&ip_dst)) {
-		mac_addr = ip6_to_multicast_mac (&ip_dst, mac);
+		mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
 	}
 	else {
 		// Check if the MAC address is already cached
 		if (n) {
 			if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
-				memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */
+				mac_addr = n->mac;		/* found it */
 		} else if (!is_ip6addr_in_my_net(&ip_dst)) {
 			struct router *gw;
 			gw = ipv6_get_default_router(&ip6h->src);