From tlb@tlb.org  Wed Sep 14 20:13:23 2005
Return-Path: <tlb@tlb.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id BFB1916A41F
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 14 Sep 2005 20:13:23 +0000 (GMT)
	(envelope-from tlb@tlb.org)
Received: from anybots.com (w047.z064220186.sjc-ca.dsl.cnc.net [64.220.186.47])
	by mx1.FreeBSD.org (Postfix) with SMTP id 7E5E943D49
	for <FreeBSD-gnats-submit@freebsd.org>; Wed, 14 Sep 2005 20:13:23 +0000 (GMT)
	(envelope-from tlb@tlb.org)
Received: (qmail 71520 invoked from network); 14 Sep 2005 20:13:23 -0000
Received: from unknown (HELO ?10.10.10.20?) (10.10.10.20)
  by 10.10.10.20 with SMTP; 14 Sep 2005 20:13:23 -0000
Message-Id: <1126728802.42486.3239.camel@lab>
Date: Wed, 14 Sep 2005 13:13:22 -0700
From: Trevor Blackwell <tlb@tlb.org>
To: FreeBSD-gnats-submit@freebsd.org
Subject: Fwd: Latent buffer overflow in getcwd

>Number:         86135
>Category:       kern
>Synopsis:       [libc] [patch] Latent buffer overflow in getcwd
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Sep 14 20:20:21 GMT 2005
>Closed-Date:    Tue Oct 04 14:37:09 GMT 2005
>Last-Modified:  Tue Oct 04 14:37:09 GMT 2005
>Originator:     Trevor Blackwell
>Release:        FreeBSD 5.4-RELEASE-p6 i386
>Organization:
Anybots Inc
>Environment:
System: FreeBSD tlb.anybots.com 5.4-RELEASE-p6 FreeBSD 5.4-RELEASE-p6 #0: Mon Sep 12 17:14:32 PDT 2005 root@tlb.anybots.com:/usr/obj/usr/src/sys/TLB i386


>Description:

The libc getcwd has a latent bug, where it allocates a buffer of 1020 bytes and assumes 
it to have MAXPATHLEN (=1024) bytes. Normal modern mallocs will allocate 1024 bytes
anyway, but a different malloc could cause an overrun, and changing MAXPATHLEN could cause
trouble, and it'll cause trouble with debugging mallocs.

Allocating 1024-4 was an optimization assuming the existence of a malloc header, which
isn't the case nowadays. The most important think is that eup = up + upsize, but the most
robust plan is to allocate MAXPATHLEN bytes in case that changes.

>How-To-Repeat:
       It wouldn't be easy to cause an actual corruption

>Fix:

Index: getcwd.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/gen/getcwd.c,v
retrieving revision 1.25
diff -c -r1.25 getcwd.c
*** getcwd.c    29 Oct 2003 10:45:01 -0000      1.25
--- getcwd.c    14 Sep 2005 18:25:48 -0000
***************
*** 115,123 ****
         * Should always be enough (it's 340 levels).  If it's not, allocate
         * as necessary.  Special case the first stat, it's ".", not "..".
         */
!       if ((up = malloc(upsize = 1024 - 4)) == NULL)
                goto err;
!       eup = up + MAXPATHLEN;
        bup = up;
        up[0] = '.';
        up[1] = '\0';
--- 115,123 ----
         * Should always be enough (it's 340 levels).  If it's not, allocate
         * as necessary.  Special case the first stat, it's ".", not "..".
         */
!       if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
                goto err;
!       eup = up + upsize;
        bup = up;
        up[0] = '.';
        up[1] = '\0';



>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->patched 
State-Changed-By: ache 
State-Changed-When: Wed Sep 14 20:38:14 GMT 2005 
State-Changed-Why:  
Fixed in -current 

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

From: Bruce Evans <bde@zeta.org.au>
To: Trevor Blackwell <tlb@tlb.org>
Cc: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Subject: Re: bin/86135: Fwd: Latent buffer overflow in getcwd
Date: Thu, 15 Sep 2005 13:27:03 +1000 (EST)

 On Wed, 14 Sep 2005, Trevor Blackwell wrote:
 
 >> Description:
 >
 > The libc getcwd has a latent bug, where it allocates a buffer of 1020 bytes and assumes
 > it to have MAXPATHLEN (=1024) bytes. Normal modern mallocs will allocate 1024 bytes
 > anyway, but a different malloc could cause an overrun, and changing MAXPATHLEN could cause
 > trouble, and it'll cause trouble with debugging mallocs.
 >
 > Allocating 1024-4 was an optimization assuming the existence of a malloc header, which
 > isn't the case nowadays. The most important think is that eup = up + upsize, but the most
 > robust plan is to allocate MAXPATHLEN bytes in case that changes.
 >
 >> How-To-Repeat:
 >       It wouldn't be easy to cause an actual corruption
 >
 >> Fix:
 >
 > Index: getcwd.c
 > ===================================================================
 > RCS file: /home/ncvs/src/lib/libc/gen/getcwd.c,v
 > retrieving revision 1.25
 > diff -c -r1.25 getcwd.c
 > *** getcwd.c    29 Oct 2003 10:45:01 -0000      1.25
 > --- getcwd.c    14 Sep 2005 18:25:48 -0000
 > ***************
 > *** 115,123 ****
 >         * Should always be enough (it's 340 levels).  If it's not, allocate
 >         * as necessary.  Special case the first stat, it's ".", not "..".
 >         */
 > !       if ((up = malloc(upsize = 1024 - 4)) == NULL)
 >                goto err;
 > !       eup = up + MAXPATHLEN;
 >        bup = up;
 >        up[0] = '.';
 >        up[1] = '\0';
 > --- 115,123 ----
 >         * Should always be enough (it's 340 levels).  If it's not, allocate
 >         * as necessary.  Special case the first stat, it's ".", not "..".
 >         */
 > !       if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
 >                goto err;
 > !       eup = up + upsize;
 >        bup = up;
 >        up[0] = '.';
 >        up[1] = '\0';
 >
 
 I prefer to change only " - 4" to "" and MAXPATHLEN to upsize (" - 4" also
 needs to be removed in the comment).  This would match similar code
 involving ept and ptsize and keep the comment in sync with the code.
 MAXPATHLEN is not very relevant here -- the size needed is just the size of
 our buffer, and MAXPATHLEN bytes is neither usually necessary nor always
 sufficient, especially for "up", since as the preceding comment says, the
 buffer for "up is [just] for holding concatenations of "../".  (This comment
 is not quite correct.  The final path component of "up" can be any directory
 entry when a mount point is crossed.)  phk might say that the whole
 memory allocation stategy is wrong.  It might be better to allocate a huge
 buffer for "up" and never reallocate it.
 
 The committed version changes (1024 - 4) to MAXPATHLEN globally.  This
 creates lots of style bugs:
 - MAXPATHLEN is a misspelling of {PATH_MAX}.
 - MAXPATHLEN is a misspelling of 1024 IMO (see above).
 - The magic 340 in the above was (1024 - 4) / strlen("../").  Now its
    magic is deeper.  340 was wrong even when the initial upsize was known
    to be (1024 - 4) since it didn't allow for the NUL terminator or mount
    points.  The exact is something like
    1 + (initial_upsize - {NAME_MAX} - 1) / strlen("../").
 
 Nearby style bug: we use the doubling strategy for expanding "pt".  This
 is especially silly if the caller passed a silly initial size for it.
 
 Bruce

From: Andrey Chernov <ache@FreeBSD.ORG>
To: Bruce Evans <bde@zeta.org.au>
Cc: Trevor Blackwell <tlb@tlb.org>, freebsd-bugs@FreeBSD.ORG,
        FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: bin/86135: Fwd: Latent buffer overflow in getcwd
Date: Thu, 15 Sep 2005 08:35:53 +0400

 On Thu, Sep 15, 2005 at 01:27:03PM +1000, Bruce Evans wrote:
 > MAXPATHLEN is not very relevant here -- the size needed is just the size of
 > our buffer, and MAXPATHLEN bytes is neither usually necessary nor always
 
 While it can be so for "up", it is not so for "ep", since it is
 filled by __getcwd() syscall and can't be bigger.
 
 Could you consider MAXPATHLEN for "ep" and 1024 for "up" variant?
 
 > - MAXPATHLEN is a misspelling of {PATH_MAX}.
 
 It is BSDsm. getwd(1) refers to MAXPATHLEN too.
 
 > - The magic 340 in the above was (1024 - 4) / strlen("../").  Now its
 >   magic is deeper.  340 was wrong even when the initial upsize was known
 >   to be (1024 - 4) since it didn't allow for the NUL terminator or mount
 >   points.  The exact is something like
 >   1 + (initial_upsize - {NAME_MAX} - 1) / strlen("../").
 
 Why ever this magic needed? It is only in comment, not in code.
 
 -- 
 http://ache.pp.ru/

From: Andrey Chernov <ache@FreeBSD.ORG>
To: Bruce Evans <bde@zeta.org.au>, Trevor Blackwell <tlb@tlb.org>,
        freebsd-bugs@FreeBSD.ORG, FreeBSD-gnats-submit@FreeBSD.ORG
Cc:  
Subject: Re: bin/86135: Fwd: Latent buffer overflow in getcwd
Date: Thu, 15 Sep 2005 08:48:47 +0400

 On Thu, Sep 15, 2005 at 08:35:53AM +0400, Andrey Chernov wrote:
 > While it can be so for "up", it is not so for "ep", since it is
 > filled by __getcwd() syscall and can't be bigger.
 
 I mean common normal situation, i.e. when __getcwd() returns 0.
 Allocating anything else than MAXPATHLEN in that situation will 
 be a waste.
 
 -- 
 http://ache.pp.ru/

From: Bruce Evans <bde@zeta.org.au>
To: Andrey Chernov <ache@FreeBSD.org>
Cc: Trevor Blackwell <tlb@tlb.org>, freebsd-bugs@FreeBSD.org,
        FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/86135: Fwd: Latent buffer overflow in getcwd
Date: Thu, 15 Sep 2005 20:21:58 +1000 (EST)

 On Thu, 15 Sep 2005, Andrey Chernov wrote:
 
 > On Thu, Sep 15, 2005 at 01:27:03PM +1000, Bruce Evans wrote:
 >> MAXPATHLEN is not very relevant here -- the size needed is just the size of
 >> our buffer, and MAXPATHLEN bytes is neither usually necessary nor always
 >
 > While it can be so for "up", it is not so for "ep", since it is
 > filled by __getcwd() syscall and can't be bigger.
 >
 > Could you consider MAXPATHLEN for "ep" and 1024 for "up" variant?
 
 The buffer with "ep" is actually "pt" ("ept" is the end of this).  Yes,
 it makes no sense to allocate less than {PATH_MAX} bytes for the buffer
 with which we make a syscall that might return {PATH_MAX} bytes.
 
 >> - MAXPATHLEN is a misspelling of {PATH_MAX}.
 >
 > It is BSDsm. getwd(1) refers to MAXPATHLEN too.
 
 imp@ is fixing this BSDism these (except he uses PATH_MAX instead of
 {PATH_MAX} so the change is only a style fix) and might not like having
 new ones to fix.
 
 BTW, the ERRORS section in getcwd(3) doesn't say that errno is set to
 ENAMETOOLONG if the MAXPATHLEN limit is exceeded.
 
 >> - The magic 340 in the above was (1024 - 4) / strlen("../").  Now its
 >>   magic is deeper.  340 was wrong even when the initial upsize was known
 >>   to be (1024 - 4) since it didn't allow for the NUL terminator or mount
 >>   points.  The exact is something like
 >>   1 + (initial_upsize - {NAME_MAX} - 1) / strlen("../").
 >
 > Why ever this magic needed? It is only in comment, not in code.
 
 It is perhaps useful as documentation, but not if it is wrong.  I try not
 to put derived magic numbers or their derivation in comments.
 
 Bruce
State-Changed-From-To: patched->closed 
State-Changed-By: ache 
State-Changed-When: Tue Oct 4 14:36:49 GMT 2005 
State-Changed-Why:  
Committed into -stable 

http://www.freebsd.org/cgi/query-pr.cgi?pr=86135 
>Unformatted:
