Subject: Re: misc/29696 : NetBSD freezes when accessing smbfs under linux emulation [Patch included]
To: None <misc-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Kailash Sethuraman <hsaliak@kasba.ath.cx>
List: netbsd-bugs
Date: 04/16/2005 15:07:01
The following reply was made to PR misc/29696; it has been noted by GNATS.

From: Kailash Sethuraman <hsaliak@kasba.ath.cx>
To: gnats-bugs@NetBSD.org
Cc: misc-bug-people@NetBSD.org
Subject: Re: misc/29696 : NetBSD freezes when accessing smbfs under linux emulation [Patch included]
Date: Sat, 16 Apr 2005 23:06:45 +0800

 Hi, 
 
 I encountered the same problem when using opera on the smbfs recently.
 The issue lies in 
 src/sys/compat/linux/common/linux_file64.c 
 ident linux_file64.c
 linux_file64.c:
      $NetBSD: linux_file64.c,v 1.22 2003/08/10 20:16:27 jdolecek Exp $
 
 The offending function : linux_sys_getdents64 
 The crash is caused by a null pointer dereference in line 486. 
 
 off = *cookie++;
 
 As indicated by VOP_READDIR's man page it seems that cookie support is optional,
 in fact it says its only for NFS. However a quick inspection showed that both
 ufs and ext2fs and have support for this. smbfs doesnt, and hence the problem 
 surfaces as the linux compat code assumes that the cookiebuf returned is
 not NULL.
 
 I have included a patch that fixes the code and hence should no longer cause a 
 crash when this code path is taken on filesystems without this VOP_READDIR
 cookie support. I have tested this on NetBSD 2.0 i386.  
 
 Unfortunately, I have also noticed that this the getdents* code is similar 
 across many emulations (such as Sunos)  and hence I believe that the other
 emulations may 
 require similar patches. I can send similar patches but I will not be able
 to test them, as I only have i386 hardware. 
 
 I also hope that this is the desired fix for the problem, rather than say 
 adding the cookie support to smbfs and other filesystems without it. 
 
 PS: Sorry for not providing a patch against the file in -current, as I do not
 track it. 
 
 Regards,
 Kailash 
 
 src/sys/compat/linux/common/linux_file64.c 
 
 --- linux_file64.c.orig	2005-04-16 21:09:19.000000000 +0800
 +++ linux_file64.c	2005-04-16 22:46:12.000000000 +0800
 @@ -474,16 +474,20 @@
  			panic("linux_readdir");
  		if (bdp->d_fileno == 0) {
  			inp += reclen;	/* it is a hole; squish it out */
 -			off = *cookie++;
 +			if(cookie)
 +				off = *cookie++;
 +			else
 +				off += reclen;
  			continue;
  		}
 +
  		linux_reclen = LINUX_RECLEN(&idb, bdp->d_namlen);
  		if (reclen > len || resid < linux_reclen) {
  			/* entry too big for buffer, so just stop */
  			outp++;
  			break;
  		}
 -		off = *cookie++;	/* each entry points to next */
 +		
  		/*
  		 * Massage in place to make a Linux-shaped dirent (otherwise
  		 * we have to worry about touching user memory outside of
 @@ -491,7 +495,9 @@
  		 */
  		idb.d_ino = bdp->d_fileno;
  		idb.d_type = bdp->d_type;
 -		idb.d_off = off;
 +		idb.d_off =  (cookie) 
 +			? (linux_off_t) *cookie  
 +			: (linux_off_t)(off + reclen);
  		idb.d_reclen = (u_short)linux_reclen;
  		strcpy(idb.d_name, bdp->d_name);
  		if ((error = copyout((caddr_t)&idb, outp, linux_reclen)))
 @@ -501,6 +507,11 @@
  		/* advance output past Linux-shaped entry */
  		outp += linux_reclen;
  		resid -= linux_reclen;
 +
 +		if(cookie)
 +			 off = *cookie++;
 +		else 
 +			off += reclen;
  	}
  
  	/* if we squished out the whole block, try again */