NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: can not download IMAP messages with isync/mbsync



> Or is UINT_MAX not guaranteed to fit in size_t

I _think_ there is no guarantee that UINT_MAX fits in a size_t.  But,
upthread, I see...

> Turn out, on ARM, strnlen(3) is written in assembly and this always
> returns `maxlen' for any value of `maxlen' > ~1GB.

Not quite.

I have a guest login on a 9.1 machine, and found the ARM strnlen there.
I am not an ARM expert, but I know it enough to, I think, find and
explain the bug.

My guess is that the buffer you're testing with is near the top of the
address space, within ~1GB of address 0xffffffff, and what you're
seeing is due to wraparound.

Here's the relevant code (from 9.1):

        adds    r5, r0, r1              /* get ptr to end of string */
        mov     r4, r1                  /* save maxlen */
...
.Lmain_loop:
#ifdef STRNLEN
        cmp     r0, r5                  /* gone too far? */
        bge     .Lmaxed_out             /*   yes, return maxlen */
#endif
(The code at .Lmaxed_out just returns maxlen, as the comment implies.)

Back-translating loosely into C, what we have here is

	strnlen(const char *buf, int maxlen)
	{
		const char *end;

		end = buf + maxlen;
		...
		while (1) {
			if (buf > end)
				return(maxlen);
			...
		}
	}

This back-translation is, of course, broken from a C perspective, but
it's supposed to be illustrative, not precise.  The bug: if buf+maxlen
overflows (at the machine-code level, on ARM32, buf, maxlen, and end
are each just 32-bit integers), then buf>end can be true right from the
start, terminating the loop (and returning maxlen) before it should.

The 9.1 manpage for strnlen says

     The strnlen() function returns either the same result as strlen() or
     maxlen, whichever is smaller.

which makes this a violation of its spec.  The only way it could be
non-broken is if size_t's range and the address space layout
collaborate to ensure that string + maxlen can't wrap around.  Since I
think both are 32-bit on (32-bit) ARM, this isn't so.

Also,

-			uint maxlen = UINT_MAX;
+			uint maxlen = sizeof(buf);

if maxlen is passed unchanged to strnlen, I can't see how the original
code isn't a bug; there's no point in using strnlen if you're pass a
maxlen greater than the space remaining in the buffer your pointer
points into.  I'd have to look at more of the surrounding code to be
sure, though.  (It also depends on nonportable assumptions about the
relative sizes of uint and size_t, but that bug is concealed on 32-bit
NetBSD.)

/~\ The ASCII				  Mouse
\ / Ribbon Campaign
 X  Against HTML		mouse%rodents-montreal.org@localhost
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B


Home | Main Index | Thread Index | Old Index