Re: malloc(0) returns an invalid address

From: JINMEI Tatuya / 職6柑巳6柑達哉 <jinmei_at_isl.rdc.toshiba.co.jp>
Date: Wed, 01 Dec 2004 15:35:39 +0900
>>>>> On Tue, 30 Nov 2004 21:40:25 -0800, 
>>>>> "David Schwartz" <davids_at_webmaster.com> said:

>> % ./a.out
>> address of p is 0x800
>> zsh: 794 segmentation fault (core dumped)  ./a.out
>> 
>> Is this a malloc bug?  Or is this the intended behavior and the man
>> page description is old?

> 	This is the intended behavior but the man page description is correct. The
> problem is not that the pointer is invalid but that you assumed that it was
> large enough to hold a 'char' and it is not.

I know the line of XXX is not correct; I simply tried to highlight the
problem, but I seem to convey the real point.  How about this example?

main()
{
	char *p = malloc(0), *q;

	printf("address of p is %p\n", p);
	printf("the value of p is %c\n", *p);
}

the execution of this code would be like this:

% ./a.out
address of p is 0x800
zsh: 645 segmentation fault (core dumped)  ./a.out

And, more specifically, my real-world problem is that 'ndp -r' fails
when it calls sysctl without any IPv6 default routers.  The related
code of ndp is as follows:

	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };

  (...)

	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
		/*NOTREACHED*/
	}
	buf = malloc(l);
	if (!buf) {
		err(1, "malloc");
		/*NOTREACHED*/
	}
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
		/*NOTREACHED*/
	}

The first call to sysctl sets 'l' to 0, since the list is empty.  Then
the malloc returns '0x800' as a *valid pointer*.  But in the second
call to sysctl, kernel rejects this pointer at line 1299 of
sys/kern/kern_sysctl.c:

userland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
    size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
{

	(...)

	if (old) {
-->		if (!useracc(old, req.oldlen, VM_PROT_WRITE))
-->			return (EFAULT);

and so we'll see

% ndp -r
ndp: sysctl(ICMPV6CTL_ND6_DRLIST): Bad address

Note that the same code worked with, e.g., FreeBSD 4.10.

So, if we wanted to call 0x800 "a valid pointer just with
not-enough-size", it would be fine.  But then we need to implement the
same logic in the kernel to provide consistent behavior.  (I would
"fix" the malloc behavior though).

					JINMEI, Tatuya
					Communication Platform Lab.
					Corporate R&D Center, Toshiba Corp.
					jinmei_at_isl.rdc.toshiba.co.jp
Received on Wed Dec 01 2004 - 05:35:30 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:23 UTC