[Openswan dev] ARM unaligned bug

D. Hugh Redelmeier hugh at mimosa.com
Tue Jan 19 15:09:36 EST 2010


| From: Albert Veli <albert.veli at gmail.com>

| So I compiled in debug information and traced it with GDB to the
| function out_modify_previous_np() in lib/libpluto/packet.c. The inner
| loop (begins on line 1675) looks like this:
| 
| ...
| struct isakmp_generic *hdr;
| for (offset = sizeof(struct isakmp_hdr); offset < len ;
| 	offset += ntohs(hdr->isag_length)) {
| 	if ((len - offset) < sizeof(struct isakmp_generic))
| 		return FALSE;
| 	hdr = (struct isakmp_generic *)(outs->start+offset);
| 	if ((len - offset) < ntohs(hdr->isag_length))
| 		return FALSE;
| 	if ((len - offset) == ntohs(hdr->isag_length)) {
| 		hdr->isag_np = np;
| 		return TRUE;
| 	}
| }
| ...
| 
| I couldn't beleive my eyes, but the problem was hdr->isag_length. When
| (outs->start+offset) is on a non-aligned address ARM runs into trouble
| and for some reason returns 0 instead of the isag_length value.
| Because of that the for loop never terminates.

packet.c routines were carefully written to not depend on alignment.
Then this one was added.  As you point out, it is broken.

| My fix was to tell the compiler that struct isakmp_generic might point
| to a non aligned address:

That will do the job but that isn't the best way to do it.

Here's a version that attempts to do this right.  I have not even
compiled the code let alone tested it so it is surely wrong.

/* Find last complete top-level payload and change its np (or fail) */
bool
out_modify_previous_np(u_int8_t np, pb_stream *outs)
{
    size_t left = (outs->cur - outs->start);
    size_t offset = 0;

    for (;;) {
		if (left < sizeof(struct isakmp_hdr)) {
			return FALSE;	/* fail: no room for another payload */
		} else {
			size_t pllen = (outs->start[offset + offsetof(struct isakmp_generic, isag_length)] << 8)
			               | outs->start[offset + offsetof(struct isakmp_generic, isag_length) + 1]

			if (left < pllen) {
				return FALSE;	/* fail: not enough room for this payload */
			} else if (left == pllen) {
				/* found last top-level payload */
				outs->start[offset + offsetof(struct isakmp_generic, isag_np)] = np;
				return TRUE;
			} else {
				/* this payload is not the last: scan forward */
				offset += pllen;
				left -= pllen;
			}
		}
	}
}


More information about the Dev mailing list