From nobody@FreeBSD.org  Mon Nov 17 02:05:00 2008
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 0C420106567A
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 17 Nov 2008 02:05:00 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id EF65D8FC1C
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 17 Nov 2008 02:04:59 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.3/8.14.3) with ESMTP id mAH24xX0015329
	for <freebsd-gnats-submit@FreeBSD.org>; Mon, 17 Nov 2008 02:04:59 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.3/8.14.3/Submit) id mAH24xkw015323;
	Mon, 17 Nov 2008 02:04:59 GMT
	(envelope-from nobody)
Message-Id: <200811170204.mAH24xkw015323@www.freebsd.org>
Date: Mon, 17 Nov 2008 02:04:59 GMT
From: James Vega <vega.james@gmail.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: realpath(3) does not follow SUS specification for a NULL/empty path
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         128933
>Category:       kern
>Synopsis:       [libc] realpath(3) does not follow SUS specification for ENOENT / ENOTDIR conditions
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kib
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Nov 17 02:10:02 UTC 2008
>Closed-Date:    Fri Jun 01 16:27:51 UTC 2012
>Last-Modified:  Wed Sep 12 15:50:08 UTC 2012
>Originator:     James Vega
>Release:        
>Organization:
>Environment:
>Description:
According to the Single Unix Specification[0], the realpath stdlib function should return NULL (to indicate an error) when the first argument to realpath is either NULL or an empty string and set errno to EINVAL/ENOENT respectively.

For the empty string case, FreeBSD is currently populating resolved with the current working directory and returning the pointer to that.

For the NULL case, I see no check whether path is NULL or not.  Instead, the first use of it is dereferencing the pointer

  if (path[0] == '/'

[0] - http://www.opengroup.org/onlinepubs/009695399/functions/realpath.html
>How-To-Repeat:

>Fix:


>Release-Note:
>Audit-Trail:

From: Andrey Chernov <ache@nagual.pp.ru>
To: James Vega <vega.james@gmail.com>
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: kern/128933: realpath(3) does not follow SUS specification for
	a NULL/empty path
Date: Tue, 18 Nov 2008 12:06:12 +0300

 On Mon, Nov 17, 2008 at 02:04:59AM +0000, James Vega wrote:
 > According to the Single Unix Specification[0], the realpath stdlib function should return NULL (to indicate an error) when the first argument to realpath is either NULL or an empty string and set errno to EINVAL/ENOENT respectively.
 > 
 > For the empty string case, FreeBSD is currently populating resolved with the current working directory and returning the pointer to that.
 > 
 > For the NULL case, I see no check whether path is NULL or not.  Instead, the first use of it is dereferencing the pointer
 > 
 >   if (path[0] == '/'
 > 
 > [0] - http://www.opengroup.org/onlinepubs/009695399/functions/realpath.html
 
 The situation is even worse. realpath(3) breaks those SUS rules 
 ("shall fail if") too:
 
 [ENOENT] A component of file_name does not name an existing file 
 [ENOTDIR] A component of the path prefix is not a directory
 
 i.e. realpath(3) does not detect fictional file name (last component) 
 with ENOENT and does not detect intermediate non-directories components 
 with ENOTDIR.
 
 See following examples with realpath(1):
 
 # realpath /non_existent
 /non_existent
 (should be "No such file or directory")
 
 
 # realpath /bin/non_existent/cp
 realpath: /bin/non_existent/cp: No such file or directory
 (should be: "Not a directory", as early as non_existent checked)
 
 -- 
 http://ache.pp.ru/

From: "Arjan van Leeuwen" <freebsd-maintainer@opera.com>
To: bug-followup@freebsd.org, vega.james@gmail.com
Cc:  
Subject: Re: kern/128933: [libc] realpath(3) does not follow SUS specification
 for a NULL/empty path
Date: Wed, 27 Oct 2010 16:13:33 +0200

 We ran into this recently. This is a real problem in portable code, since  
 it's not possible to assume that realpath() will notify the caller if a  
 file doesn't exist, even though it should according to the specification.
 
 Arjan
 
 -- 
 Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Responsible-Changed-From-To: freebsd-bugs->kib 
Responsible-Changed-By: arundel 
Responsible-Changed-When: Wed Oct 27 17:51:58 UTC 2010 
Responsible-Changed-Why:  
Please note that the NULL issue has already been fixed by r206893 and PR 121897 
was closed. The issue Andrey Chernov described in his followup to this PR 
however remains to be fixed. 
Kib, could you take a look at this? 

http://www.freebsd.org/cgi/query-pr.cgi?pr=128933 

From: Kostik Belousov <kostikbel@gmail.com>
To: bug-followup@FreeBSD.org, vega.james@gmail.com
Cc:  
Subject: Re: kern/128933: [libc] realpath(3) does not follow SUS specification
 for ENOENT / ENOTDIR conditions
Date: Mon, 7 May 2012 14:49:26 +0300

 Quick look makes me think that the following change is enough.
 
 diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
 index 2c9562e..f038759 100644
 --- a/lib/libc/stdlib/realpath.c
 +++ b/lib/libc/stdlib/realpath.c
 @@ -163,10 +163,8 @@ realpath(const char * __restrict path, char *
 __restrict resolved)
  			return (NULL);
  		}
  		if (lstat(resolved, &sb) != 0) {
 -			if (errno == ENOENT && p == NULL) {
 -				errno = serrno;
 -				return (resolved);
 -			}
 +			if (errno != ENOENT || p != NULL)
 +				errno = ENOTDIR;
  			if (m)
  				free(resolved);
  			return (NULL);

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/128933: commit references a PR
Date: Fri,  1 Jun 2012 14:44:43 +0000 (UTC)

 Author: kib
 Date: Fri Jun  1 14:40:16 2012
 New Revision: 236400
 URL: http://svn.freebsd.org/changeset/base/236400
 
 Log:
   MFC r235266:
   According to SUSv4, realpath(3) must fail if
     [ENOENT]  A component of file_name does not name an existing file or
         file_name points to an empty string.
     [ENOTDIR] A component of the path prefix is not a directory, or the
         file_name argument contains at least one non- <slash> character
         and ends with one or more trailing <slash> characters and the last
         pathname component names an existing file that is neither a
         directory nor a symbolic link to a directory.
   Add checks for the listed conditions, and set errno accordingly.
   
   Update the realpath(3) manpage to mention SUS behaviour. Remove the
   requirement to include sys/param.h before stdlib.h.
   
   PR:	128933
 
 Modified:
   stable/9/lib/libc/stdlib/realpath.3
   stable/9/lib/libc/stdlib/realpath.c
 Directory Properties:
   stable/9/lib/libc/   (props changed)
 
 Modified: stable/9/lib/libc/stdlib/realpath.3
 ==============================================================================
 --- stable/9/lib/libc/stdlib/realpath.3	Fri Jun  1 14:29:59 2012	(r236399)
 +++ stable/9/lib/libc/stdlib/realpath.3	Fri Jun  1 14:40:16 2012	(r236400)
 @@ -31,7 +31,7 @@
  .\"     @(#)realpath.3	8.2 (Berkeley) 2/16/94
  .\" $FreeBSD$
  .\"
 -.Dd April 19, 2010
 +.Dd May 11, 2012
  .Dt REALPATH 3
  .Os
  .Sh NAME
 @@ -40,7 +40,6 @@
  .Sh LIBRARY
  .Lb libc
  .Sh SYNOPSIS
 -.In sys/param.h
  .In stdlib.h
  .Ft "char *"
  .Fn realpath "const char *pathname" "char *resolved_path"
 @@ -72,11 +71,12 @@ The
  function will resolve both absolute and relative paths
  and return the absolute pathname corresponding to
  .Fa pathname .
 -All but the last component of
 +All components of
  .Fa pathname
  must exist when
  .Fn realpath
 -is called.
 +is called, and all but the last component must name either directories or
 +symlinks pointing to the directories.
  .Sh "RETURN VALUES"
  The
  .Fn realpath
 
 Modified: stable/9/lib/libc/stdlib/realpath.c
 ==============================================================================
 --- stable/9/lib/libc/stdlib/realpath.c	Fri Jun  1 14:29:59 2012	(r236399)
 +++ stable/9/lib/libc/stdlib/realpath.c	Fri Jun  1 14:40:16 2012	(r236400)
 @@ -132,8 +132,29 @@ realpath(const char * __restrict path, c
  			resolved[resolved_len++] = '/';
  			resolved[resolved_len] = '\0';
  		}
 -		if (next_token[0] == '\0')
 +		if (next_token[0] == '\0') {
 +			/*
 +			 * Handle consequential slashes.  The path
 +			 * before slash shall point to a directory.
 +			 *
 +			 * Only the trailing slashes are not covered
 +			 * by other checks in the loop, but we verify
 +			 * the prefix for any (rare) "//" or "/\0"
 +			 * occurence to not implement lookahead.
 +			 */
 +			if (lstat(resolved, &sb) != 0) {
 +				if (m)
 +					free(resolved);
 +				return (NULL);
 +			}
 +			if (!S_ISDIR(sb.st_mode)) {
 +				if (m)
 +					free(resolved);
 +				errno = ENOTDIR;
 +				return (NULL);
 +			}
  			continue;
 +		}
  		else if (strcmp(next_token, ".") == 0)
  			continue;
  		else if (strcmp(next_token, "..") == 0) {
 @@ -151,9 +172,7 @@ realpath(const char * __restrict path, c
  		}
  
  		/*
 -		 * Append the next path component and lstat() it. If
 -		 * lstat() fails we still can return successfully if
 -		 * there are no more path components left.
 +		 * Append the next path component and lstat() it.
  		 */
  		resolved_len = strlcat(resolved, next_token, PATH_MAX);
  		if (resolved_len >= PATH_MAX) {
 @@ -163,10 +182,8 @@ realpath(const char * __restrict path, c
  			return (NULL);
  		}
  		if (lstat(resolved, &sb) != 0) {
 -			if (errno == ENOENT && p == NULL) {
 -				errno = serrno;
 -				return (resolved);
 -			}
 +			if (errno != ENOENT || p != NULL)
 +				errno = ENOTDIR;
  			if (m)
  				free(resolved);
  			return (NULL);
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: kib 
State-Changed-When: Fri Jun 1 16:27:27 UTC 2012 
State-Changed-Why:  
Committed to HEAD and RELENG_9. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=128933 

From: Eric Blake <eblake@redhat.com>
To: bug-followup@FreeBSD.org, vega.james@gmail.com
Cc:  
Subject: Re: kern/128933: [libc] realpath(3) does not follow SUS specification
 for ENOENT / ENOTDIR conditions
Date: Wed, 12 Sep 2012 09:42:41 -0600

 This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
 --------------enigF318BBDFEDB7B9631E9BFB7D
 Content-Type: text/plain; charset=UTF-8
 Content-Transfer-Encoding: quoted-printable
 
 The fix applied for this bug appears to have mis-interpreted SUS, in a
 manner incompatible with glibc and Solaris implementations, and was
 detected as a failure in the gnulib testsuite, as mentioned in this
 thread[1].
 
 [1]https://lists.gnu.org/archive/html/bug-gnulib/2012-09/msg00079.html
 
 As already mentioned, SUS requires realpath to fail with ENOENT if a
 component does not exist, and with ENOTDIR if a component is not a
 directory.  But the SUS (POSIX) wording is intended that a file can be
 detected only as not being a directory if it already exists as some
 other file type, since ENOENT was already reserved for the case of not
 existing.  Therefore, I disagree with your argument of:
 
 > # realpath /bin/non_existent/cp
 > realpath: /bin/non_existent/cp: No such file or directory
 > (should be: "Not a directory", as early as non_existent checked)
 
 and instead, argue that it should be:
 
 # realpath /bin/non_existent/cp
 realpath: /bin/non_existent/cp: No such file or directory
 (correct - since /bin/non_existent does not exist, ENOENT trumps ENOTDIR)=
 
 
 # realpath /bin/cp/cp
 realpath: /bin/cp/cp: Not a directory
 (since /bin/cp exists, but is not a directory, /bin/cp/cp cannot be check=
 ed)
 
 --=20
 Eric Blake   eblake@redhat.com    +1-919-301-3266
 Libvirt virtualization library http://libvirt.org
 
 
 --------------enigF318BBDFEDB7B9631E9BFB7D
 Content-Type: application/pgp-signature; name="signature.asc"
 Content-Description: OpenPGP digital signature
 Content-Disposition: attachment; filename="signature.asc"
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 Comment: Public key at http://people.redhat.com/eblake/eblake.gpg
 Comment: Using GnuPG with Mozilla - http://www.enigmail.net/
 
 iQEcBAEBCAAGBQJQUK1xAAoJEKeha0olJ0Nqp9kH+weFmFzhR2UW+SaP+h49Diym
 kR+zsF/wfW0qBJcb/02BpBCp/dW1sUqAPR4otz31bvKYtD7LnNZQO1FXMj1Xlw71
 acWBolpsRckYZhp3CnY0crueDUUon4IGHZVDst1ZGzsYSsqIEoEJpktl4GqQULBh
 wlyNe9TKX28ajO3f9mmvTdQNoRR02zRmaAzPZCyJpuDDh/BjsE9JN4QtxppeM8Sr
 nR4pifkRAZgkSuBAy94czeAhl+448Do1u4w724W8E5JARp9iC3RzO7D/YYmavifd
 iIi0yuR2jdftCArCe851vL57lScyIgsq757LO0ugads+0G0yZiK+Tupfkd6DdOU=
 =Y4jL
 -----END PGP SIGNATURE-----
 
 --------------enigF318BBDFEDB7B9631E9BFB7D--
>Unformatted:
