Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Nov 1999 17:48:31 +0100
From:      Pierre Beyssac <beyssac@enst.fr>
To:        freebsd-current@freebsd.org
Subject:   egcs -O breaks ping.c:in_cksum()
Message-ID:  <19991115174831.B30139@enst.fr>

next in thread | raw e-mail | index | archive | help

--azLHFNyN32YCQGCU
Content-Type: text/plain; charset=us-ascii

I've discovered the following problem, either due to egcs or the
source code for in_cksum in ping, I'm not sure.

The symptom is that in_cksum() returns an invalid result on an
odd-size buffer, when compile optimization is on.

You can check this by trying "ping -s 65 localhost" and seeing that
no reply is ever received. The kernel ICMP bad checksum count just
increases.

The problem is apparently due to the following code fragment:

        register u_short answer = 0;
[...]
        /* mop up an odd byte, if necessary */
        if (nleft == 1) {
                *(u_char *)(&answer) = *(u_char *)w ;
                sum += answer;
        }

Removing the "register" declaration for 'answer' doesn't help.
OTOH, adding "volatile" does help and seems to fix the problem.

Only I'm not sure that's the correct fix because I'm unsure about
the exact semantics of "volatile"; it might well be an egcs bug.

Attached is a test program if anyone wishes to experiment.
Try to compile with and without -O and see the difference.

The correct output is "cksum=f9f6", the wrong output is "cksum=f5f6".
-- 
Pierre Beyssac		pb@enst.fr

--azLHFNyN32YCQGCU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ck.c"

#include <sys/types.h>
#include <stdio.h>

/*
 * in_cksum --
 *	Checksum routine for Internet Protocol family headers (C Version)
 */
u_short
in_cksum(addr, len)
	u_short *addr;
	int len;
{
	register int nleft = len;
	register u_short *w = addr;
	int sum = 0;
	volatile u_short answer = 0;

	/*
	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
	 * sequential 16 bit words to it, and at the end, fold back all the
	 * carry bits from the top 16 bits into the lower 16 bits.
	 */
	while (nleft > 1)  {
		sum += *w++;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1) {
		*(u_char *)(&answer) = *(u_char *)w ;
		sum += answer;
	}

	/* add back carry outs from top 16 bits to low 16 bits */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return(answer);
}

int main()
{
	unsigned char tb[] = { 1, 2, 3, 4, 5 };
	printf("cksum=%04x\n", in_cksum(tb, sizeof tb));
}

--azLHFNyN32YCQGCU--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19991115174831.B30139>